blob: 496ef7d446940935e0b063114a6128c38c2fb9c1 [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"
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +000027#include "clang/StaticAnalyzer/Checkers/ObjCRetainCount.h"
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +000028#include "clang/AST/Attr.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000029#include "llvm/ADT/SmallString.h"
30
31using namespace clang;
32using namespace arcmt;
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +000033using namespace ento::objc_retain;
Ted Kremenekf7639e12012-03-06 20:06:33 +000034
35namespace {
36
37class ObjCMigrateASTConsumer : public ASTConsumer {
38 void migrateDecl(Decl *D);
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000039 void migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCInterfaceDecl *D);
Fariborz Jahanian1be01532013-07-12 22:32:19 +000040 void migrateProtocolConformance(ASTContext &Ctx,
41 const ObjCImplementationDecl *ImpDecl);
Fariborz Jahanian92463272013-07-18 20:11:45 +000042 void migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl,
43 const TypedefDecl *TypedefDcl);
Fariborz Jahanian71221352013-07-23 22:42:28 +000044 void migrateInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl);
Fariborz Jahanian670ef262013-07-23 23:34:42 +000045 void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl,
46 ObjCMethodDecl *OM);
Fariborz Jahanianc4291852013-08-02 18:00:53 +000047 void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl,
Fariborz Jahanian9275c682013-08-02 20:54:18 +000048 ObjCMethodDecl *OM,
49 ObjCInstanceTypeFamily OIT_Family = OIT_None);
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +000050
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +000051 void migrateCFFunctions(ASTContext &Ctx,
52 const FunctionDecl *FuncDecl);
53
54 bool migrateAddFunctionAnnotation(ASTContext &Ctx,
55 const FunctionDecl *FuncDecl);
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +000056
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +000057 void migrateObjCMethodDeclAnnotation(ASTContext &Ctx,
58 const ObjCMethodDecl *MethodDecl);
Ted Kremenekf7639e12012-03-06 20:06:33 +000059public:
60 std::string MigrateDir;
61 bool MigrateLiterals;
62 bool MigrateSubscripting;
Fariborz Jahaniand83ef842013-07-09 16:59:14 +000063 bool MigrateProperty;
Dmitri Gribenkof8579502013-01-12 19:30:44 +000064 OwningPtr<NSAPI> NSAPIObj;
65 OwningPtr<edit::EditedSource> Editor;
Ted Kremenekf7639e12012-03-06 20:06:33 +000066 FileRemapper &Remapper;
67 FileManager &FileMgr;
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +000068 const PPConditionalDirectiveRecord *PPRec;
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000069 Preprocessor &PP;
Ted Kremenekf7639e12012-03-06 20:06:33 +000070 bool IsOutputFile;
Fariborz Jahanian1be01532013-07-12 22:32:19 +000071 llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls;
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +000072 llvm::SmallVector<const FunctionDecl *, 8> CFFunctionIBCandidates;
Fariborz Jahanian1be01532013-07-12 22:32:19 +000073
Ted Kremenekf7639e12012-03-06 20:06:33 +000074 ObjCMigrateASTConsumer(StringRef migrateDir,
75 bool migrateLiterals,
76 bool migrateSubscripting,
Fariborz Jahaniand83ef842013-07-09 16:59:14 +000077 bool migrateProperty,
Ted Kremenekf7639e12012-03-06 20:06:33 +000078 FileRemapper &remapper,
79 FileManager &fileMgr,
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +000080 const PPConditionalDirectiveRecord *PPRec,
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000081 Preprocessor &PP,
Ted Kremenekf7639e12012-03-06 20:06:33 +000082 bool isOutputFile = false)
83 : MigrateDir(migrateDir),
84 MigrateLiterals(migrateLiterals),
85 MigrateSubscripting(migrateSubscripting),
Fariborz Jahaniand83ef842013-07-09 16:59:14 +000086 MigrateProperty(migrateProperty),
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000087 Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),
Ted Kremenekf7639e12012-03-06 20:06:33 +000088 IsOutputFile(isOutputFile) { }
89
90protected:
91 virtual void Initialize(ASTContext &Context) {
92 NSAPIObj.reset(new NSAPI(Context));
93 Editor.reset(new edit::EditedSource(Context.getSourceManager(),
David Blaikiebbafb8a2012-03-11 07:00:24 +000094 Context.getLangOpts(),
Ted Kremenekf7639e12012-03-06 20:06:33 +000095 PPRec));
96 }
97
98 virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
99 for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
100 migrateDecl(*I);
101 return true;
102 }
103 virtual void HandleInterestingDecl(DeclGroupRef DG) {
104 // Ignore decls from the PCH.
105 }
106 virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
107 ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
108 }
109
110 virtual void HandleTranslationUnit(ASTContext &Ctx);
111};
112
113}
114
115ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction,
116 StringRef migrateDir,
117 bool migrateLiterals,
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000118 bool migrateSubscripting,
119 bool migrateProperty)
Ted Kremenekf7639e12012-03-06 20:06:33 +0000120 : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir),
121 MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting),
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000122 MigrateProperty(migrateProperty),
Ted Kremenekf7639e12012-03-06 20:06:33 +0000123 CompInst(0) {
124 if (MigrateDir.empty())
125 MigrateDir = "."; // user current directory if none is given.
126}
127
128ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI,
129 StringRef InFile) {
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +0000130 PPConditionalDirectiveRecord *
131 PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
132 CompInst->getPreprocessor().addPPCallbacks(PPRec);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000133 ASTConsumer *
134 WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
135 ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir,
136 MigrateLiterals,
137 MigrateSubscripting,
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000138 MigrateProperty,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000139 Remapper,
140 CompInst->getFileManager(),
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000141 PPRec,
142 CompInst->getPreprocessor());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000143 ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer };
144 return new MultiplexConsumer(Consumers);
145}
146
147bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
148 Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
149 /*ignoreIfFilesChanges=*/true);
150 CompInst = &CI;
151 CI.getDiagnostics().setIgnoreAllWarnings(true);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000152 return true;
153}
154
155namespace {
156class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
157 ObjCMigrateASTConsumer &Consumer;
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000158 ParentMap &PMap;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000159
160public:
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000161 ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
162 : Consumer(consumer), PMap(PMap) { }
Ted Kremenekf7639e12012-03-06 20:06:33 +0000163
164 bool shouldVisitTemplateInstantiations() const { return false; }
165 bool shouldWalkTypesOfTypeLocs() const { return false; }
166
167 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
168 if (Consumer.MigrateLiterals) {
169 edit::Commit commit(*Consumer.Editor);
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000170 edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000171 Consumer.Editor->commit(commit);
172 }
173
174 if (Consumer.MigrateSubscripting) {
175 edit::Commit commit(*Consumer.Editor);
176 edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
177 Consumer.Editor->commit(commit);
178 }
179
180 return true;
181 }
182
183 bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
184 // Do depth first; we want to rewrite the subexpressions first so that if
185 // we have to move expressions we will move them already rewritten.
186 for (Stmt::child_range range = E->children(); range; ++range)
187 if (!TraverseStmt(*range))
188 return false;
189
190 return WalkUpFromObjCMessageExpr(E);
191 }
192};
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000193
194class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
195 ObjCMigrateASTConsumer &Consumer;
196 OwningPtr<ParentMap> PMap;
197
198public:
199 BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
200
201 bool shouldVisitTemplateInstantiations() const { return false; }
202 bool shouldWalkTypesOfTypeLocs() const { return false; }
203
204 bool TraverseStmt(Stmt *S) {
205 PMap.reset(new ParentMap(S));
206 ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
207 return true;
208 }
209};
Ted Kremenekf7639e12012-03-06 20:06:33 +0000210}
211
212void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
213 if (!D)
214 return;
215 if (isa<ObjCMethodDecl>(D))
216 return; // Wait for the ObjC container declaration.
217
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000218 BodyMigrator(*this).TraverseDecl(D);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000219}
220
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000221static void append_attr(std::string &PropertyString, const char *attr,
222 bool GetterHasIsPrefix) {
223 PropertyString += (GetterHasIsPrefix ? ", " : "(");
224 PropertyString += attr;
225 PropertyString += ')';
226}
227
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000228static bool rewriteToObjCProperty(const ObjCMethodDecl *Getter,
229 const ObjCMethodDecl *Setter,
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000230 const NSAPI &NS, edit::Commit &commit,
231 bool GetterHasIsPrefix) {
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000232 ASTContext &Context = NS.getASTContext();
233 std::string PropertyString = "@property";
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000234 std::string PropertyNameString = Getter->getNameAsString();
235 StringRef PropertyName(PropertyNameString);
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000236 if (GetterHasIsPrefix) {
237 PropertyString += "(getter=";
238 PropertyString += PropertyNameString;
239 }
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000240 // Short circuit properties that contain the name "delegate" or "dataSource",
241 // or have exact name "target" to have unsafe_unretained attribute.
242 if (PropertyName.equals("target") ||
243 (PropertyName.find("delegate") != StringRef::npos) ||
244 (PropertyName.find("dataSource") != StringRef::npos))
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000245 append_attr(PropertyString, "unsafe_unretained", GetterHasIsPrefix);
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000246 else {
247 const ParmVarDecl *argDecl = *Setter->param_begin();
248 QualType ArgType = Context.getCanonicalType(argDecl->getType());
249 Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();
250 bool RetainableObject = ArgType->isObjCRetainableType();
251 if (RetainableObject && propertyLifetime == Qualifiers::OCL_Strong) {
252 if (const ObjCObjectPointerType *ObjPtrTy =
253 ArgType->getAs<ObjCObjectPointerType>()) {
254 ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
255 if (IDecl &&
256 IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying")))
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000257 append_attr(PropertyString, "copy", GetterHasIsPrefix);
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000258 else
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000259 append_attr(PropertyString, "retain", GetterHasIsPrefix);
260 } else if (GetterHasIsPrefix)
261 PropertyString += ')';
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000262 } else if (propertyLifetime == Qualifiers::OCL_Weak)
263 // TODO. More precise determination of 'weak' attribute requires
264 // looking into setter's implementation for backing weak ivar.
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000265 append_attr(PropertyString, "weak", GetterHasIsPrefix);
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000266 else if (RetainableObject)
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000267 append_attr(PropertyString, "retain", GetterHasIsPrefix);
268 else if (GetterHasIsPrefix)
269 PropertyString += ')';
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000270 }
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000271
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000272 QualType RT = Getter->getResultType();
273 if (!isa<TypedefType>(RT)) {
274 // strip off any ARC lifetime qualifier.
275 QualType CanResultTy = Context.getCanonicalType(RT);
276 if (CanResultTy.getQualifiers().hasObjCLifetime()) {
277 Qualifiers Qs = CanResultTy.getQualifiers();
278 Qs.removeObjCLifetime();
279 RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs);
280 }
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000281 }
282 PropertyString += " ";
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000283 PropertyString += RT.getAsString(Context.getPrintingPolicy());
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000284 PropertyString += " ";
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000285 if (GetterHasIsPrefix) {
286 // property name must strip off "is" and lower case the first character
287 // after that; e.g. isContinuous will become continuous.
288 StringRef PropertyNameStringRef(PropertyNameString);
289 PropertyNameStringRef = PropertyNameStringRef.drop_front(2);
290 PropertyNameString = PropertyNameStringRef;
291 std::string NewPropertyNameString = PropertyNameString;
Fariborz Jahaniandb8bf832013-08-08 21:51:06 +0000292 NewPropertyNameString[0] = toLowercase(NewPropertyNameString[0]);
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000293 PropertyString += NewPropertyNameString;
294 }
295 else
296 PropertyString += PropertyNameString;
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000297 commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(),
298 Getter->getDeclaratorEndLoc()),
299 PropertyString);
300 SourceLocation EndLoc = Setter->getDeclaratorEndLoc();
301 // Get location past ';'
302 EndLoc = EndLoc.getLocWithOffset(1);
303 commit.remove(CharSourceRange::getCharRange(Setter->getLocStart(), EndLoc));
304 return true;
305}
306
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000307void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx,
308 ObjCInterfaceDecl *D) {
309 for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end();
310 M != MEnd; ++M) {
311 ObjCMethodDecl *Method = (*M);
Fariborz Jahaniande661002013-07-03 23:44:11 +0000312 if (Method->isPropertyAccessor() || Method->param_size() != 0)
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000313 continue;
314 // Is this method candidate to be a getter?
Fariborz Jahaniande661002013-07-03 23:44:11 +0000315 QualType GRT = Method->getResultType();
316 if (GRT->isVoidType())
317 continue;
Fariborz Jahanian7ac20e12013-07-08 22:49:25 +0000318 // FIXME. Don't know what todo with attributes, skip for now.
319 if (Method->hasAttrs())
320 continue;
321
Fariborz Jahaniande661002013-07-03 23:44:11 +0000322 Selector GetterSelector = Method->getSelector();
323 IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);
324 Selector SetterSelector =
325 SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
326 PP.getSelectorTable(),
327 getterName);
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000328 ObjCMethodDecl *SetterMethod = D->lookupMethod(SetterSelector, true);
329 bool GetterHasIsPrefix = false;
330 if (!SetterMethod) {
331 // try a different naming convention for getter: isXxxxx
332 StringRef getterNameString = getterName->getName();
Fariborz Jahanian261fdb72013-08-08 21:20:01 +0000333 if (getterNameString.startswith("is") && !GRT->isObjCRetainableType()) {
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000334 GetterHasIsPrefix = true;
335 const char *CGetterName = getterNameString.data() + 2;
Fariborz Jahanian261fdb72013-08-08 21:20:01 +0000336 if (CGetterName[0] && isUppercase(CGetterName[0])) {
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000337 getterName = &Ctx.Idents.get(CGetterName);
338 SetterSelector =
339 SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
340 PP.getSelectorTable(),
341 getterName);
342 SetterMethod = D->lookupMethod(SetterSelector, true);
343 }
344 }
345 }
346 if (SetterMethod) {
Fariborz Jahaniande661002013-07-03 23:44:11 +0000347 // Is this a valid setter, matching the target getter?
348 QualType SRT = SetterMethod->getResultType();
349 if (!SRT->isVoidType())
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000350 continue;
Fariborz Jahaniande661002013-07-03 23:44:11 +0000351 const ParmVarDecl *argDecl = *SetterMethod->param_begin();
Fariborz Jahanian43bbaac2013-07-04 00:24:32 +0000352 QualType ArgType = argDecl->getType();
Fariborz Jahanian7ac20e12013-07-08 22:49:25 +0000353 if (!Ctx.hasSameUnqualifiedType(ArgType, GRT) ||
354 SetterMethod->hasAttrs())
Fariborz Jahanian43bbaac2013-07-04 00:24:32 +0000355 continue;
Fariborz Jahanian266926d2013-07-05 20:46:03 +0000356 edit::Commit commit(*Editor);
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000357 rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit,
358 GetterHasIsPrefix);
Fariborz Jahanian266926d2013-07-05 20:46:03 +0000359 Editor->commit(commit);
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000360 }
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000361 }
362}
363
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000364static bool
Fariborz Jahanian9a3512b2013-07-13 17:16:41 +0000365ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000366 const ObjCImplementationDecl *ImpDecl,
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000367 const ObjCInterfaceDecl *IDecl,
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000368 ObjCProtocolDecl *Protocol) {
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000369 // In auto-synthesis, protocol properties are not synthesized. So,
370 // a conforming protocol must have its required properties declared
371 // in class interface.
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000372 bool HasAtleastOneRequiredProperty = false;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000373 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition())
374 for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(),
375 E = PDecl->prop_end(); P != E; ++P) {
376 ObjCPropertyDecl *Property = *P;
377 if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional)
378 continue;
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000379 HasAtleastOneRequiredProperty = true;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000380 DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName());
Fariborz Jahanian2bc3dda2013-07-16 21:59:42 +0000381 if (R.size() == 0) {
382 // Relax the rule and look into class's implementation for a synthesize
383 // or dynamic declaration. Class is implementing a property coming from
384 // another protocol. This still makes the target protocol as conforming.
385 if (!ImpDecl->FindPropertyImplDecl(
386 Property->getDeclName().getAsIdentifierInfo()))
387 return false;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000388 }
Fariborz Jahanian2bc3dda2013-07-16 21:59:42 +0000389 else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) {
390 if ((ClassProperty->getPropertyAttributes()
391 != Property->getPropertyAttributes()) ||
392 !Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
393 return false;
394 }
395 else
396 return false;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000397 }
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000398
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000399 // At this point, all required properties in this protocol conform to those
400 // declared in the class.
401 // Check that class implements the required methods of the protocol too.
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000402 bool HasAtleastOneRequiredMethod = false;
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000403 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) {
404 if (PDecl->meth_begin() == PDecl->meth_end())
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000405 return HasAtleastOneRequiredProperty;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000406 for (ObjCContainerDecl::method_iterator M = PDecl->meth_begin(),
407 MEnd = PDecl->meth_end(); M != MEnd; ++M) {
408 ObjCMethodDecl *MD = (*M);
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000409 if (MD->isImplicit())
410 continue;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000411 if (MD->getImplementationControl() == ObjCMethodDecl::Optional)
412 continue;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000413 DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName());
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000414 if (R.size() == 0)
415 return false;
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000416 bool match = false;
417 HasAtleastOneRequiredMethod = true;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000418 for (unsigned I = 0, N = R.size(); I != N; ++I)
419 if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0]))
420 if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {
421 match = true;
422 break;
423 }
424 if (!match)
425 return false;
426 }
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000427 }
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000428 if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod)
429 return true;
430 return false;
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000431}
432
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000433static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl,
434 llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols,
435 const NSAPI &NS, edit::Commit &commit) {
436 const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols();
437 std::string ClassString;
438 SourceLocation EndLoc =
439 IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation();
440
441 if (Protocols.empty()) {
442 ClassString = '<';
443 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
444 ClassString += ConformingProtocols[i]->getNameAsString();
445 if (i != (e-1))
446 ClassString += ", ";
447 }
448 ClassString += "> ";
449 }
450 else {
451 ClassString = ", ";
452 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
453 ClassString += ConformingProtocols[i]->getNameAsString();
454 if (i != (e-1))
455 ClassString += ", ";
456 }
457 ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1;
458 EndLoc = *PL;
459 }
460
461 commit.insertAfterToken(EndLoc, ClassString);
462 return true;
463}
464
465static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,
466 const TypedefDecl *TypedefDcl,
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000467 const NSAPI &NS, edit::Commit &commit,
468 bool IsNSIntegerType) {
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000469 std::string ClassString =
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000470 IsNSIntegerType ? "typedef NS_ENUM(NSInteger, " : "typedef NS_OPTIONS(NSUInteger, ";
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000471 ClassString += TypedefDcl->getIdentifier()->getName();
472 ClassString += ')';
473 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
474 commit.replace(R, ClassString);
475 SourceLocation EndOfTypedefLoc = TypedefDcl->getLocEnd();
476 EndOfTypedefLoc = trans::findLocationAfterSemi(EndOfTypedefLoc, NS.getASTContext());
477 if (!EndOfTypedefLoc.isInvalid()) {
478 commit.remove(SourceRange(TypedefDcl->getLocStart(), EndOfTypedefLoc));
479 return true;
480 }
481 return false;
482}
483
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000484static bool rewriteToNSMacroDecl(const EnumDecl *EnumDcl,
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000485 const TypedefDecl *TypedefDcl,
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000486 const NSAPI &NS, edit::Commit &commit,
487 bool IsNSIntegerType) {
488 std::string ClassString =
489 IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, ";
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000490 ClassString += TypedefDcl->getIdentifier()->getName();
491 ClassString += ')';
492 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
493 commit.replace(R, ClassString);
494 SourceLocation TypedefLoc = TypedefDcl->getLocEnd();
495 commit.remove(SourceRange(TypedefLoc, TypedefLoc));
496 return true;
497}
498
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000499static bool UseNSOptionsMacro(ASTContext &Ctx,
500 const EnumDecl *EnumDcl) {
501 bool PowerOfTwo = true;
Fariborz Jahanianbe7bc112013-08-15 18:46:37 +0000502 uint64_t MaxPowerOfTwoVal = 0;
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000503 for (EnumDecl::enumerator_iterator EI = EnumDcl->enumerator_begin(),
504 EE = EnumDcl->enumerator_end(); EI != EE; ++EI) {
505 EnumConstantDecl *Enumerator = (*EI);
506 const Expr *InitExpr = Enumerator->getInitExpr();
507 if (!InitExpr) {
508 PowerOfTwo = false;
509 continue;
510 }
511 InitExpr = InitExpr->IgnoreImpCasts();
512 if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
513 if (BO->isShiftOp() || BO->isBitwiseOp())
514 return true;
515
516 uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
Fariborz Jahanianbe7bc112013-08-15 18:46:37 +0000517 if (PowerOfTwo && EnumVal) {
518 if (!llvm::isPowerOf2_64(EnumVal))
519 PowerOfTwo = false;
520 else if (EnumVal > MaxPowerOfTwoVal)
521 MaxPowerOfTwoVal = EnumVal;
522 }
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000523 }
Fariborz Jahanian0b48e7c2013-08-15 18:58:00 +0000524 return PowerOfTwo && (MaxPowerOfTwoVal > 2);
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000525}
526
Fariborz Jahanian008ef722013-07-19 17:44:32 +0000527void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000528 const ObjCImplementationDecl *ImpDecl) {
529 const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();
530 if (!IDecl || ObjCProtocolDecls.empty())
531 return;
532 // Find all implicit conforming protocols for this class
533 // and make them explicit.
534 llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols;
535 Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols);
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000536 llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols;
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000537
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000538 for (llvm::SmallPtrSet<ObjCProtocolDecl*, 32>::iterator I =
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000539 ObjCProtocolDecls.begin(),
540 E = ObjCProtocolDecls.end(); I != E; ++I)
541 if (!ExplicitProtocols.count(*I))
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000542 PotentialImplicitProtocols.push_back(*I);
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000543
544 if (PotentialImplicitProtocols.empty())
545 return;
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000546
547 // go through list of non-optional methods and properties in each protocol
548 // in the PotentialImplicitProtocols list. If class implements every one of the
549 // methods and properties, then this class conforms to this protocol.
550 llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols;
551 for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000552 if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl,
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000553 PotentialImplicitProtocols[i]))
554 ConformingProtocols.push_back(PotentialImplicitProtocols[i]);
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000555
556 if (ConformingProtocols.empty())
557 return;
Fariborz Jahaniancb7b8de2013-07-17 00:02:22 +0000558
559 // Further reduce number of conforming protocols. If protocol P1 is in the list
560 // protocol P2 (P2<P1>), No need to include P1.
561 llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols;
562 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
563 bool DropIt = false;
564 ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i];
565 for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {
566 ObjCProtocolDecl *PDecl = ConformingProtocols[i1];
567 if (PDecl == TargetPDecl)
568 continue;
569 if (PDecl->lookupProtocolNamed(
570 TargetPDecl->getDeclName().getAsIdentifierInfo())) {
571 DropIt = true;
572 break;
573 }
574 }
575 if (!DropIt)
576 MinimalConformingProtocols.push_back(TargetPDecl);
577 }
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000578 edit::Commit commit(*Editor);
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000579 rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols,
580 *NSAPIObj, commit);
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000581 Editor->commit(commit);
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000582}
583
Fariborz Jahanian92463272013-07-18 20:11:45 +0000584void ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx,
585 const EnumDecl *EnumDcl,
586 const TypedefDecl *TypedefDcl) {
587 if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() ||
588 !TypedefDcl->getIdentifier())
589 return;
590
591 QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000592 bool IsNSIntegerType = NSAPIObj->isObjCNSIntegerType(qt);
593 bool IsNSUIntegerType = !IsNSIntegerType && NSAPIObj->isObjCNSUIntegerType(qt);
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000594
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000595 if (!IsNSIntegerType && !IsNSUIntegerType) {
596 // Also check for typedef enum {...} TD;
597 if (const EnumType *EnumTy = qt->getAs<EnumType>()) {
598 if (EnumTy->getDecl() == EnumDcl) {
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000599 bool NSOptions = UseNSOptionsMacro(Ctx, EnumDcl);
600 if (NSOptions) {
601 if (!Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
602 return;
603 }
604 else if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000605 return;
606 edit::Commit commit(*Editor);
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000607 rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000608 Editor->commit(commit);
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000609 }
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000610 }
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000611 return;
612 }
613 if (IsNSIntegerType && UseNSOptionsMacro(Ctx, EnumDcl)) {
614 // We may still use NS_OPTIONS based on what we find in the enumertor list.
615 IsNSIntegerType = false;
616 IsNSUIntegerType = true;
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000617 }
Fariborz Jahanian92463272013-07-18 20:11:45 +0000618
619 // NS_ENUM must be available.
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000620 if (IsNSIntegerType && !Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
621 return;
622 // NS_OPTIONS must be available.
623 if (IsNSUIntegerType && !Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
Fariborz Jahanian92463272013-07-18 20:11:45 +0000624 return;
625 edit::Commit commit(*Editor);
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000626 rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, IsNSIntegerType);
Fariborz Jahanian92463272013-07-18 20:11:45 +0000627 Editor->commit(commit);
628}
629
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000630static void ReplaceWithInstancetype(const ObjCMigrateASTConsumer &ASTC,
631 ObjCMethodDecl *OM) {
632 SourceRange R;
633 std::string ClassString;
634 if (TypeSourceInfo *TSInfo = OM->getResultTypeSourceInfo()) {
635 TypeLoc TL = TSInfo->getTypeLoc();
636 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc());
637 ClassString = "instancetype";
638 }
639 else {
640 R = SourceRange(OM->getLocStart(), OM->getLocStart());
641 ClassString = OM->isInstanceMethod() ? '-' : '+';
642 ClassString += " (instancetype)";
643 }
644 edit::Commit commit(*ASTC.Editor);
645 commit.replace(R, ClassString);
646 ASTC.Editor->commit(commit);
647}
648
Fariborz Jahanian670ef262013-07-23 23:34:42 +0000649void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx,
650 ObjCContainerDecl *CDecl,
651 ObjCMethodDecl *OM) {
Fariborz Jahanian71221352013-07-23 22:42:28 +0000652 ObjCInstanceTypeFamily OIT_Family =
653 Selector::getInstTypeMethodFamily(OM->getSelector());
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000654
Fariborz Jahanian267bae32013-07-24 19:18:37 +0000655 std::string ClassName;
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000656 switch (OIT_Family) {
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000657 case OIT_None:
658 migrateFactoryMethod(Ctx, CDecl, OM);
659 return;
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000660 case OIT_Array:
Fariborz Jahanian267bae32013-07-24 19:18:37 +0000661 ClassName = "NSArray";
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000662 break;
663 case OIT_Dictionary:
Fariborz Jahanian267bae32013-07-24 19:18:37 +0000664 ClassName = "NSDictionary";
665 break;
666 case OIT_MemManage:
667 ClassName = "NSObject";
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000668 break;
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000669 case OIT_Singleton:
670 migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton);
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000671 return;
672 }
Fariborz Jahanian71221352013-07-23 22:42:28 +0000673 if (!OM->getResultType()->isObjCIdType())
674 return;
675
676 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
677 if (!IDecl) {
678 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
679 IDecl = CatDecl->getClassInterface();
680 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
681 IDecl = ImpDecl->getClassInterface();
682 }
Fariborz Jahanian267bae32013-07-24 19:18:37 +0000683 if (!IDecl ||
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000684 !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) {
685 migrateFactoryMethod(Ctx, CDecl, OM);
Fariborz Jahanian71221352013-07-23 22:42:28 +0000686 return;
Fariborz Jahanian280954a2013-07-24 18:31:42 +0000687 }
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000688 ReplaceWithInstancetype(*this, OM);
Fariborz Jahanian71221352013-07-23 22:42:28 +0000689}
690
691void ObjCMigrateASTConsumer::migrateInstanceType(ASTContext &Ctx,
692 ObjCContainerDecl *CDecl) {
693 // migrate methods which can have instancetype as their result type.
694 for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(),
695 MEnd = CDecl->meth_end();
696 M != MEnd; ++M) {
697 ObjCMethodDecl *Method = (*M);
698 migrateMethodInstanceType(Ctx, CDecl, Method);
699 }
700}
701
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000702void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx,
703 ObjCContainerDecl *CDecl,
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000704 ObjCMethodDecl *OM,
705 ObjCInstanceTypeFamily OIT_Family) {
Fariborz Jahanian3bfc35e2013-08-02 22:34:18 +0000706 if (OM->isInstanceMethod() ||
707 OM->getResultType() == Ctx.getObjCInstanceType() ||
708 !OM->getResultType()->isObjCIdType())
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000709 return;
710
711 // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class
712 // NSYYYNamE with matching names be at least 3 characters long.
713 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
714 if (!IDecl) {
715 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
716 IDecl = CatDecl->getClassInterface();
717 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
718 IDecl = ImpDecl->getClassInterface();
719 }
720 if (!IDecl)
721 return;
722
723 std::string StringClassName = IDecl->getName();
724 StringRef LoweredClassName(StringClassName);
725 std::string StringLoweredClassName = LoweredClassName.lower();
726 LoweredClassName = StringLoweredClassName;
727
728 IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0);
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +0000729 // Handle method with no name at its first selector slot; e.g. + (id):(int)x.
730 if (!MethodIdName)
731 return;
732
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000733 std::string MethodName = MethodIdName->getName();
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000734 if (OIT_Family == OIT_Singleton) {
735 StringRef STRefMethodName(MethodName);
736 size_t len = 0;
737 if (STRefMethodName.startswith("standard"))
738 len = strlen("standard");
739 else if (STRefMethodName.startswith("shared"))
740 len = strlen("shared");
741 else if (STRefMethodName.startswith("default"))
742 len = strlen("default");
743 else
744 return;
745 MethodName = STRefMethodName.substr(len);
746 }
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000747 std::string MethodNameSubStr = MethodName.substr(0, 3);
748 StringRef MethodNamePrefix(MethodNameSubStr);
749 std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower();
750 MethodNamePrefix = StringLoweredMethodNamePrefix;
751 size_t Ix = LoweredClassName.rfind(MethodNamePrefix);
752 if (Ix == StringRef::npos)
753 return;
754 std::string ClassNamePostfix = LoweredClassName.substr(Ix);
755 StringRef LoweredMethodName(MethodName);
756 std::string StringLoweredMethodName = LoweredMethodName.lower();
757 LoweredMethodName = StringLoweredMethodName;
758 if (!LoweredMethodName.startswith(ClassNamePostfix))
759 return;
760 ReplaceWithInstancetype(*this, OM);
761}
762
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000763static bool IsVoidStarType(QualType Ty) {
764 if (!Ty->isPointerType())
765 return false;
766
767 while (const TypedefType *TD = dyn_cast<TypedefType>(Ty.getTypePtr()))
768 Ty = TD->getDecl()->getUnderlyingType();
769
770 // Is the type void*?
771 const PointerType* PT = Ty->getAs<PointerType>();
772 if (PT->getPointeeType().getUnqualifiedType()->isVoidType())
773 return true;
774 return IsVoidStarType(PT->getPointeeType());
775}
776
777
778static bool
779IsCFFunctionImplicitBridingingCandidate(ASTContext &Ctx,
780 const FunctionDecl *FuncDecl) {
781 CallEffects CE = CallEffects::getEffect(FuncDecl);
782
783 RetEffect Ret = CE.getReturnValue();
784 if (Ret.getObjKind() == RetEffect::CF)
785 // Still a candidate;
786 ;
787 else if (Ret.getObjKind() == RetEffect::AnyObj &&
788 (Ret.getKind() == RetEffect::NoRet || Ret.getKind() == RetEffect::NoRetHard)) {
789 // This is a candidate as long as it is not of "void *" variety
790 if (IsVoidStarType(FuncDecl->getResultType()))
791 return false;
792 }
793
794 // FIXME. Check on the arguments too.
795
796 // FIXME. Always false for now.
797 return false;
798}
799
800void ObjCMigrateASTConsumer::migrateCFFunctions(
801 ASTContext &Ctx,
802 const FunctionDecl *FuncDecl) {
803 // Finction must be annotated first.
804 bool Annotated = migrateAddFunctionAnnotation(Ctx, FuncDecl);
805 if (Annotated && IsCFFunctionImplicitBridingingCandidate(Ctx, FuncDecl))
806 CFFunctionIBCandidates.push_back(FuncDecl);
807}
808
809bool ObjCMigrateASTConsumer::migrateAddFunctionAnnotation(
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +0000810 ASTContext &Ctx,
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +0000811 const FunctionDecl *FuncDecl) {
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +0000812 if (FuncDecl->hasAttr<CFAuditedTransferAttr>() ||
813 FuncDecl->getAttr<CFReturnsRetainedAttr>() ||
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000814 FuncDecl->getAttr<CFReturnsNotRetainedAttr>())
815 return true;
816 if (FuncDecl->hasBody())
817 return false;
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +0000818
819 CallEffects CE = CallEffects::getEffect(FuncDecl);
820 RetEffect Ret = CE.getReturnValue();
821 const char *AnnotationString = 0;
822 if (Ret.getObjKind() == RetEffect::CF && Ret.isOwned()) {
823 if (!Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition())
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000824 return false;
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +0000825 AnnotationString = " CF_RETURNS_RETAINED";
826 }
827 else if (Ret.getObjKind() == RetEffect::CF && !Ret.isOwned()) {
828 if (!Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition())
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000829 return false;
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +0000830 AnnotationString = " CF_RETURNS_NOT_RETAINED";
831 }
832 else
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000833 return false;
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +0000834
835 edit::Commit commit(*Editor);
836 commit.insertAfterToken(FuncDecl->getLocEnd(), AnnotationString);
837 Editor->commit(commit);
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000838 return true;
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +0000839}
840
841void ObjCMigrateASTConsumer::migrateObjCMethodDeclAnnotation(
842 ASTContext &Ctx,
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +0000843 const ObjCMethodDecl *MethodDecl) {
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +0000844 if (MethodDecl->hasAttr<CFAuditedTransferAttr>() ||
845 MethodDecl->getAttr<CFReturnsRetainedAttr>() ||
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +0000846 MethodDecl->getAttr<CFReturnsNotRetainedAttr>() ||
847 MethodDecl->hasBody())
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +0000848 return;
849}
850
Ted Kremenekf7639e12012-03-06 20:06:33 +0000851namespace {
852
853class RewritesReceiver : public edit::EditsReceiver {
854 Rewriter &Rewrite;
855
856public:
857 RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
858
859 virtual void insert(SourceLocation loc, StringRef text) {
860 Rewrite.InsertText(loc, text);
861 }
862 virtual void replace(CharSourceRange range, StringRef text) {
863 Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
864 }
865};
866
867}
868
869void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000870
871 TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000872 if (MigrateProperty)
873 for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();
874 D != DEnd; ++D) {
875 if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))
876 migrateObjCInterfaceDecl(Ctx, CDecl);
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000877 else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D))
878 ObjCProtocolDecls.insert(PDecl);
879 else if (const ObjCImplementationDecl *ImpDecl =
880 dyn_cast<ObjCImplementationDecl>(*D))
881 migrateProtocolConformance(Ctx, ImpDecl);
Fariborz Jahanian92463272013-07-18 20:11:45 +0000882 else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {
883 DeclContext::decl_iterator N = D;
884 ++N;
Fariborz Jahanian008ef722013-07-19 17:44:32 +0000885 if (N != DEnd)
886 if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N))
887 migrateNSEnumDecl(Ctx, ED, TD);
Fariborz Jahanian92463272013-07-18 20:11:45 +0000888 }
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +0000889 else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D))
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000890 migrateCFFunctions(Ctx, FD);
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +0000891 else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(*D))
892 migrateObjCMethodDeclAnnotation(Ctx, MD);
893
Fariborz Jahanian71221352013-07-23 22:42:28 +0000894 // migrate methods which can have instancetype as their result type.
895 if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D))
896 migrateInstanceType(Ctx, CDecl);
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000897 }
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000898
David Blaikiebbafb8a2012-03-11 07:00:24 +0000899 Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000900 RewritesReceiver Rec(rewriter);
901 Editor->applyRewrites(Rec);
902
903 for (Rewriter::buffer_iterator
904 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
905 FileID FID = I->first;
906 RewriteBuffer &buf = I->second;
907 const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
908 assert(file);
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000909 SmallString<512> newText;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000910 llvm::raw_svector_ostream vecOS(newText);
911 buf.write(vecOS);
912 vecOS.flush();
913 llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
914 StringRef(newText.data(), newText.size()), file->getName());
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000915 SmallString<64> filePath(file->getName());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000916 FileMgr.FixupRelativePath(filePath);
917 Remapper.remap(filePath.str(), memBuf);
918 }
919
920 if (IsOutputFile) {
921 Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
922 } else {
923 Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
924 }
925}
926
927bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
Argyrios Kyrtzidisb4822602012-05-24 16:48:23 +0000928 CI.getDiagnostics().setIgnoreAllWarnings(true);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000929 return true;
930}
931
932ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI,
933 StringRef InFile) {
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +0000934 PPConditionalDirectiveRecord *
935 PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
936 CI.getPreprocessor().addPPCallbacks(PPRec);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000937 return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile,
938 /*MigrateLiterals=*/true,
939 /*MigrateSubscripting=*/true,
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000940 /*MigrateProperty*/true,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000941 Remapper,
942 CI.getFileManager(),
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +0000943 PPRec,
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000944 CI.getPreprocessor(),
Ted Kremenekf7639e12012-03-06 20:06:33 +0000945 /*isOutputFile=*/true);
946}