blob: 37e2214233e0e7bebf6de14d68bce248f5da0278 [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 Jahanian9ef4a262013-08-19 19:13:34 +000027#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +000028#include "clang/StaticAnalyzer/Checkers/ObjCRetainCount.h"
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +000029#include "clang/AST/Attr.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000030#include "llvm/ADT/SmallString.h"
31
32using namespace clang;
33using namespace arcmt;
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +000034using namespace ento::objc_retain;
Ted Kremenekf7639e12012-03-06 20:06:33 +000035
36namespace {
37
38class ObjCMigrateASTConsumer : public ASTConsumer {
39 void migrateDecl(Decl *D);
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000040 void migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCInterfaceDecl *D);
Fariborz Jahanian1be01532013-07-12 22:32:19 +000041 void migrateProtocolConformance(ASTContext &Ctx,
42 const ObjCImplementationDecl *ImpDecl);
Fariborz Jahanian92463272013-07-18 20:11:45 +000043 void migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl,
44 const TypedefDecl *TypedefDcl);
Fariborz Jahanian71221352013-07-23 22:42:28 +000045 void migrateInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl);
Fariborz Jahanian670ef262013-07-23 23:34:42 +000046 void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl,
47 ObjCMethodDecl *OM);
Fariborz Jahanianc4291852013-08-02 18:00:53 +000048 void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl,
Fariborz Jahanian9275c682013-08-02 20:54:18 +000049 ObjCMethodDecl *OM,
50 ObjCInstanceTypeFamily OIT_Family = OIT_None);
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +000051
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +000052 void migrateCFFunctions(ASTContext &Ctx,
53 const FunctionDecl *FuncDecl);
54
Fariborz Jahanian301b5212013-08-20 22:42:13 +000055 void AnnotateImplicitBridging(ASTContext &Ctx);
56
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +000057 bool migrateAddFunctionAnnotation(ASTContext &Ctx,
58 const FunctionDecl *FuncDecl);
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +000059
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +000060 void migrateObjCMethodDeclAnnotation(ASTContext &Ctx,
61 const ObjCMethodDecl *MethodDecl);
Ted Kremenekf7639e12012-03-06 20:06:33 +000062public:
63 std::string MigrateDir;
64 bool MigrateLiterals;
65 bool MigrateSubscripting;
Fariborz Jahaniand83ef842013-07-09 16:59:14 +000066 bool MigrateProperty;
Fariborz Jahanianb8343522013-08-20 23:35:26 +000067 unsigned FileId;
Dmitri Gribenkof8579502013-01-12 19:30:44 +000068 OwningPtr<NSAPI> NSAPIObj;
69 OwningPtr<edit::EditedSource> Editor;
Ted Kremenekf7639e12012-03-06 20:06:33 +000070 FileRemapper &Remapper;
71 FileManager &FileMgr;
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +000072 const PPConditionalDirectiveRecord *PPRec;
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000073 Preprocessor &PP;
Ted Kremenekf7639e12012-03-06 20:06:33 +000074 bool IsOutputFile;
Fariborz Jahanian1be01532013-07-12 22:32:19 +000075 llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls;
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +000076 llvm::SmallVector<const FunctionDecl *, 8> CFFunctionIBCandidates;
Fariborz Jahanian1be01532013-07-12 22:32:19 +000077
Ted Kremenekf7639e12012-03-06 20:06:33 +000078 ObjCMigrateASTConsumer(StringRef migrateDir,
79 bool migrateLiterals,
80 bool migrateSubscripting,
Fariborz Jahaniand83ef842013-07-09 16:59:14 +000081 bool migrateProperty,
Ted Kremenekf7639e12012-03-06 20:06:33 +000082 FileRemapper &remapper,
83 FileManager &fileMgr,
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +000084 const PPConditionalDirectiveRecord *PPRec,
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000085 Preprocessor &PP,
Ted Kremenekf7639e12012-03-06 20:06:33 +000086 bool isOutputFile = false)
87 : MigrateDir(migrateDir),
88 MigrateLiterals(migrateLiterals),
89 MigrateSubscripting(migrateSubscripting),
Fariborz Jahanianb8343522013-08-20 23:35:26 +000090 MigrateProperty(migrateProperty), FileId(0),
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000091 Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),
Ted Kremenekf7639e12012-03-06 20:06:33 +000092 IsOutputFile(isOutputFile) { }
93
94protected:
95 virtual void Initialize(ASTContext &Context) {
96 NSAPIObj.reset(new NSAPI(Context));
97 Editor.reset(new edit::EditedSource(Context.getSourceManager(),
David Blaikiebbafb8a2012-03-11 07:00:24 +000098 Context.getLangOpts(),
Ted Kremenekf7639e12012-03-06 20:06:33 +000099 PPRec));
100 }
101
102 virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
103 for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
104 migrateDecl(*I);
105 return true;
106 }
107 virtual void HandleInterestingDecl(DeclGroupRef DG) {
108 // Ignore decls from the PCH.
109 }
110 virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
111 ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
112 }
113
114 virtual void HandleTranslationUnit(ASTContext &Ctx);
115};
116
117}
118
119ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction,
120 StringRef migrateDir,
121 bool migrateLiterals,
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000122 bool migrateSubscripting,
123 bool migrateProperty)
Ted Kremenekf7639e12012-03-06 20:06:33 +0000124 : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir),
125 MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting),
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000126 MigrateProperty(migrateProperty),
Ted Kremenekf7639e12012-03-06 20:06:33 +0000127 CompInst(0) {
128 if (MigrateDir.empty())
129 MigrateDir = "."; // user current directory if none is given.
130}
131
132ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI,
133 StringRef InFile) {
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +0000134 PPConditionalDirectiveRecord *
135 PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
136 CompInst->getPreprocessor().addPPCallbacks(PPRec);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000137 ASTConsumer *
138 WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
139 ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir,
140 MigrateLiterals,
141 MigrateSubscripting,
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000142 MigrateProperty,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000143 Remapper,
144 CompInst->getFileManager(),
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000145 PPRec,
146 CompInst->getPreprocessor());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000147 ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer };
148 return new MultiplexConsumer(Consumers);
149}
150
151bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
152 Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
153 /*ignoreIfFilesChanges=*/true);
154 CompInst = &CI;
155 CI.getDiagnostics().setIgnoreAllWarnings(true);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000156 return true;
157}
158
159namespace {
160class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
161 ObjCMigrateASTConsumer &Consumer;
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000162 ParentMap &PMap;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000163
164public:
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000165 ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
166 : Consumer(consumer), PMap(PMap) { }
Ted Kremenekf7639e12012-03-06 20:06:33 +0000167
168 bool shouldVisitTemplateInstantiations() const { return false; }
169 bool shouldWalkTypesOfTypeLocs() const { return false; }
170
171 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
172 if (Consumer.MigrateLiterals) {
173 edit::Commit commit(*Consumer.Editor);
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000174 edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000175 Consumer.Editor->commit(commit);
176 }
177
178 if (Consumer.MigrateSubscripting) {
179 edit::Commit commit(*Consumer.Editor);
180 edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
181 Consumer.Editor->commit(commit);
182 }
183
184 return true;
185 }
186
187 bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
188 // Do depth first; we want to rewrite the subexpressions first so that if
189 // we have to move expressions we will move them already rewritten.
190 for (Stmt::child_range range = E->children(); range; ++range)
191 if (!TraverseStmt(*range))
192 return false;
193
194 return WalkUpFromObjCMessageExpr(E);
195 }
196};
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000197
198class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
199 ObjCMigrateASTConsumer &Consumer;
200 OwningPtr<ParentMap> PMap;
201
202public:
203 BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
204
205 bool shouldVisitTemplateInstantiations() const { return false; }
206 bool shouldWalkTypesOfTypeLocs() const { return false; }
207
208 bool TraverseStmt(Stmt *S) {
209 PMap.reset(new ParentMap(S));
210 ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
211 return true;
212 }
213};
Ted Kremenekf7639e12012-03-06 20:06:33 +0000214}
215
216void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
217 if (!D)
218 return;
219 if (isa<ObjCMethodDecl>(D))
220 return; // Wait for the ObjC container declaration.
221
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000222 BodyMigrator(*this).TraverseDecl(D);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000223}
224
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000225static void append_attr(std::string &PropertyString, const char *attr,
226 bool GetterHasIsPrefix) {
227 PropertyString += (GetterHasIsPrefix ? ", " : "(");
228 PropertyString += attr;
229 PropertyString += ')';
230}
231
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000232static bool rewriteToObjCProperty(const ObjCMethodDecl *Getter,
233 const ObjCMethodDecl *Setter,
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000234 const NSAPI &NS, edit::Commit &commit,
235 bool GetterHasIsPrefix) {
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000236 ASTContext &Context = NS.getASTContext();
237 std::string PropertyString = "@property";
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000238 std::string PropertyNameString = Getter->getNameAsString();
239 StringRef PropertyName(PropertyNameString);
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000240 if (GetterHasIsPrefix) {
241 PropertyString += "(getter=";
242 PropertyString += PropertyNameString;
243 }
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000244 // Short circuit properties that contain the name "delegate" or "dataSource",
245 // or have exact name "target" to have unsafe_unretained attribute.
246 if (PropertyName.equals("target") ||
247 (PropertyName.find("delegate") != StringRef::npos) ||
248 (PropertyName.find("dataSource") != StringRef::npos))
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000249 append_attr(PropertyString, "unsafe_unretained", GetterHasIsPrefix);
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000250 else {
251 const ParmVarDecl *argDecl = *Setter->param_begin();
252 QualType ArgType = Context.getCanonicalType(argDecl->getType());
253 Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();
254 bool RetainableObject = ArgType->isObjCRetainableType();
255 if (RetainableObject && propertyLifetime == Qualifiers::OCL_Strong) {
256 if (const ObjCObjectPointerType *ObjPtrTy =
257 ArgType->getAs<ObjCObjectPointerType>()) {
258 ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
259 if (IDecl &&
260 IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying")))
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000261 append_attr(PropertyString, "copy", GetterHasIsPrefix);
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000262 else
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000263 append_attr(PropertyString, "retain", GetterHasIsPrefix);
264 } else if (GetterHasIsPrefix)
265 PropertyString += ')';
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000266 } else if (propertyLifetime == Qualifiers::OCL_Weak)
267 // TODO. More precise determination of 'weak' attribute requires
268 // looking into setter's implementation for backing weak ivar.
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000269 append_attr(PropertyString, "weak", GetterHasIsPrefix);
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000270 else if (RetainableObject)
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000271 append_attr(PropertyString, "retain", GetterHasIsPrefix);
272 else if (GetterHasIsPrefix)
273 PropertyString += ')';
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000274 }
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000275
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000276 QualType RT = Getter->getResultType();
277 if (!isa<TypedefType>(RT)) {
278 // strip off any ARC lifetime qualifier.
279 QualType CanResultTy = Context.getCanonicalType(RT);
280 if (CanResultTy.getQualifiers().hasObjCLifetime()) {
281 Qualifiers Qs = CanResultTy.getQualifiers();
282 Qs.removeObjCLifetime();
283 RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs);
284 }
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000285 }
286 PropertyString += " ";
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000287 PropertyString += RT.getAsString(Context.getPrintingPolicy());
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000288 PropertyString += " ";
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000289 if (GetterHasIsPrefix) {
290 // property name must strip off "is" and lower case the first character
291 // after that; e.g. isContinuous will become continuous.
292 StringRef PropertyNameStringRef(PropertyNameString);
293 PropertyNameStringRef = PropertyNameStringRef.drop_front(2);
294 PropertyNameString = PropertyNameStringRef;
295 std::string NewPropertyNameString = PropertyNameString;
Fariborz Jahaniandb8bf832013-08-08 21:51:06 +0000296 NewPropertyNameString[0] = toLowercase(NewPropertyNameString[0]);
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000297 PropertyString += NewPropertyNameString;
298 }
299 else
300 PropertyString += PropertyNameString;
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000301 commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(),
302 Getter->getDeclaratorEndLoc()),
303 PropertyString);
304 SourceLocation EndLoc = Setter->getDeclaratorEndLoc();
305 // Get location past ';'
306 EndLoc = EndLoc.getLocWithOffset(1);
307 commit.remove(CharSourceRange::getCharRange(Setter->getLocStart(), EndLoc));
308 return true;
309}
310
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000311void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx,
312 ObjCInterfaceDecl *D) {
313 for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end();
314 M != MEnd; ++M) {
315 ObjCMethodDecl *Method = (*M);
Fariborz Jahaniande661002013-07-03 23:44:11 +0000316 if (Method->isPropertyAccessor() || Method->param_size() != 0)
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000317 continue;
318 // Is this method candidate to be a getter?
Fariborz Jahaniande661002013-07-03 23:44:11 +0000319 QualType GRT = Method->getResultType();
320 if (GRT->isVoidType())
321 continue;
Fariborz Jahanian7ac20e12013-07-08 22:49:25 +0000322 // FIXME. Don't know what todo with attributes, skip for now.
323 if (Method->hasAttrs())
324 continue;
325
Fariborz Jahaniande661002013-07-03 23:44:11 +0000326 Selector GetterSelector = Method->getSelector();
327 IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);
328 Selector SetterSelector =
329 SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
330 PP.getSelectorTable(),
331 getterName);
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000332 ObjCMethodDecl *SetterMethod = D->lookupMethod(SetterSelector, true);
333 bool GetterHasIsPrefix = false;
334 if (!SetterMethod) {
335 // try a different naming convention for getter: isXxxxx
336 StringRef getterNameString = getterName->getName();
Fariborz Jahanian261fdb72013-08-08 21:20:01 +0000337 if (getterNameString.startswith("is") && !GRT->isObjCRetainableType()) {
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000338 GetterHasIsPrefix = true;
339 const char *CGetterName = getterNameString.data() + 2;
Fariborz Jahanian261fdb72013-08-08 21:20:01 +0000340 if (CGetterName[0] && isUppercase(CGetterName[0])) {
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000341 getterName = &Ctx.Idents.get(CGetterName);
342 SetterSelector =
343 SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
344 PP.getSelectorTable(),
345 getterName);
346 SetterMethod = D->lookupMethod(SetterSelector, true);
347 }
348 }
349 }
350 if (SetterMethod) {
Fariborz Jahaniande661002013-07-03 23:44:11 +0000351 // Is this a valid setter, matching the target getter?
352 QualType SRT = SetterMethod->getResultType();
353 if (!SRT->isVoidType())
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000354 continue;
Fariborz Jahaniande661002013-07-03 23:44:11 +0000355 const ParmVarDecl *argDecl = *SetterMethod->param_begin();
Fariborz Jahanian43bbaac2013-07-04 00:24:32 +0000356 QualType ArgType = argDecl->getType();
Fariborz Jahanian7ac20e12013-07-08 22:49:25 +0000357 if (!Ctx.hasSameUnqualifiedType(ArgType, GRT) ||
358 SetterMethod->hasAttrs())
Fariborz Jahanian43bbaac2013-07-04 00:24:32 +0000359 continue;
Fariborz Jahanian266926d2013-07-05 20:46:03 +0000360 edit::Commit commit(*Editor);
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000361 rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit,
362 GetterHasIsPrefix);
Fariborz Jahanian266926d2013-07-05 20:46:03 +0000363 Editor->commit(commit);
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000364 }
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000365 }
366}
367
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000368static bool
Fariborz Jahanian9a3512b2013-07-13 17:16:41 +0000369ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000370 const ObjCImplementationDecl *ImpDecl,
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000371 const ObjCInterfaceDecl *IDecl,
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000372 ObjCProtocolDecl *Protocol) {
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000373 // In auto-synthesis, protocol properties are not synthesized. So,
374 // a conforming protocol must have its required properties declared
375 // in class interface.
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000376 bool HasAtleastOneRequiredProperty = false;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000377 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition())
378 for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(),
379 E = PDecl->prop_end(); P != E; ++P) {
380 ObjCPropertyDecl *Property = *P;
381 if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional)
382 continue;
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000383 HasAtleastOneRequiredProperty = true;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000384 DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName());
Fariborz Jahanian2bc3dda2013-07-16 21:59:42 +0000385 if (R.size() == 0) {
386 // Relax the rule and look into class's implementation for a synthesize
387 // or dynamic declaration. Class is implementing a property coming from
388 // another protocol. This still makes the target protocol as conforming.
389 if (!ImpDecl->FindPropertyImplDecl(
390 Property->getDeclName().getAsIdentifierInfo()))
391 return false;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000392 }
Fariborz Jahanian2bc3dda2013-07-16 21:59:42 +0000393 else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) {
394 if ((ClassProperty->getPropertyAttributes()
395 != Property->getPropertyAttributes()) ||
396 !Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
397 return false;
398 }
399 else
400 return false;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000401 }
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000402
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000403 // At this point, all required properties in this protocol conform to those
404 // declared in the class.
405 // Check that class implements the required methods of the protocol too.
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000406 bool HasAtleastOneRequiredMethod = false;
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000407 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) {
408 if (PDecl->meth_begin() == PDecl->meth_end())
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000409 return HasAtleastOneRequiredProperty;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000410 for (ObjCContainerDecl::method_iterator M = PDecl->meth_begin(),
411 MEnd = PDecl->meth_end(); M != MEnd; ++M) {
412 ObjCMethodDecl *MD = (*M);
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000413 if (MD->isImplicit())
414 continue;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000415 if (MD->getImplementationControl() == ObjCMethodDecl::Optional)
416 continue;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000417 DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName());
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000418 if (R.size() == 0)
419 return false;
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000420 bool match = false;
421 HasAtleastOneRequiredMethod = true;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000422 for (unsigned I = 0, N = R.size(); I != N; ++I)
423 if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0]))
424 if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {
425 match = true;
426 break;
427 }
428 if (!match)
429 return false;
430 }
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000431 }
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000432 if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod)
433 return true;
434 return false;
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000435}
436
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000437static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl,
438 llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols,
439 const NSAPI &NS, edit::Commit &commit) {
440 const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols();
441 std::string ClassString;
442 SourceLocation EndLoc =
443 IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation();
444
445 if (Protocols.empty()) {
446 ClassString = '<';
447 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
448 ClassString += ConformingProtocols[i]->getNameAsString();
449 if (i != (e-1))
450 ClassString += ", ";
451 }
452 ClassString += "> ";
453 }
454 else {
455 ClassString = ", ";
456 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
457 ClassString += ConformingProtocols[i]->getNameAsString();
458 if (i != (e-1))
459 ClassString += ", ";
460 }
461 ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1;
462 EndLoc = *PL;
463 }
464
465 commit.insertAfterToken(EndLoc, ClassString);
466 return true;
467}
468
469static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,
470 const TypedefDecl *TypedefDcl,
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000471 const NSAPI &NS, edit::Commit &commit,
472 bool IsNSIntegerType) {
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000473 std::string ClassString =
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000474 IsNSIntegerType ? "typedef NS_ENUM(NSInteger, " : "typedef NS_OPTIONS(NSUInteger, ";
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000475 ClassString += TypedefDcl->getIdentifier()->getName();
476 ClassString += ')';
477 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
478 commit.replace(R, ClassString);
479 SourceLocation EndOfTypedefLoc = TypedefDcl->getLocEnd();
480 EndOfTypedefLoc = trans::findLocationAfterSemi(EndOfTypedefLoc, NS.getASTContext());
481 if (!EndOfTypedefLoc.isInvalid()) {
482 commit.remove(SourceRange(TypedefDcl->getLocStart(), EndOfTypedefLoc));
483 return true;
484 }
485 return false;
486}
487
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000488static bool rewriteToNSMacroDecl(const EnumDecl *EnumDcl,
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000489 const TypedefDecl *TypedefDcl,
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000490 const NSAPI &NS, edit::Commit &commit,
491 bool IsNSIntegerType) {
492 std::string ClassString =
493 IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, ";
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000494 ClassString += TypedefDcl->getIdentifier()->getName();
495 ClassString += ')';
496 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
497 commit.replace(R, ClassString);
498 SourceLocation TypedefLoc = TypedefDcl->getLocEnd();
499 commit.remove(SourceRange(TypedefLoc, TypedefLoc));
500 return true;
501}
502
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000503static bool UseNSOptionsMacro(ASTContext &Ctx,
504 const EnumDecl *EnumDcl) {
505 bool PowerOfTwo = true;
Fariborz Jahanianbe7bc112013-08-15 18:46:37 +0000506 uint64_t MaxPowerOfTwoVal = 0;
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000507 for (EnumDecl::enumerator_iterator EI = EnumDcl->enumerator_begin(),
508 EE = EnumDcl->enumerator_end(); EI != EE; ++EI) {
509 EnumConstantDecl *Enumerator = (*EI);
510 const Expr *InitExpr = Enumerator->getInitExpr();
511 if (!InitExpr) {
512 PowerOfTwo = false;
513 continue;
514 }
515 InitExpr = InitExpr->IgnoreImpCasts();
516 if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
517 if (BO->isShiftOp() || BO->isBitwiseOp())
518 return true;
519
520 uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
Fariborz Jahanianbe7bc112013-08-15 18:46:37 +0000521 if (PowerOfTwo && EnumVal) {
522 if (!llvm::isPowerOf2_64(EnumVal))
523 PowerOfTwo = false;
524 else if (EnumVal > MaxPowerOfTwoVal)
525 MaxPowerOfTwoVal = EnumVal;
526 }
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000527 }
Fariborz Jahanian0b48e7c2013-08-15 18:58:00 +0000528 return PowerOfTwo && (MaxPowerOfTwoVal > 2);
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000529}
530
Fariborz Jahanian008ef722013-07-19 17:44:32 +0000531void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000532 const ObjCImplementationDecl *ImpDecl) {
533 const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();
534 if (!IDecl || ObjCProtocolDecls.empty())
535 return;
536 // Find all implicit conforming protocols for this class
537 // and make them explicit.
538 llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols;
539 Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols);
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000540 llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols;
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000541
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000542 for (llvm::SmallPtrSet<ObjCProtocolDecl*, 32>::iterator I =
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000543 ObjCProtocolDecls.begin(),
544 E = ObjCProtocolDecls.end(); I != E; ++I)
545 if (!ExplicitProtocols.count(*I))
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000546 PotentialImplicitProtocols.push_back(*I);
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000547
548 if (PotentialImplicitProtocols.empty())
549 return;
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000550
551 // go through list of non-optional methods and properties in each protocol
552 // in the PotentialImplicitProtocols list. If class implements every one of the
553 // methods and properties, then this class conforms to this protocol.
554 llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols;
555 for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000556 if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl,
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000557 PotentialImplicitProtocols[i]))
558 ConformingProtocols.push_back(PotentialImplicitProtocols[i]);
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000559
560 if (ConformingProtocols.empty())
561 return;
Fariborz Jahaniancb7b8de2013-07-17 00:02:22 +0000562
563 // Further reduce number of conforming protocols. If protocol P1 is in the list
564 // protocol P2 (P2<P1>), No need to include P1.
565 llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols;
566 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
567 bool DropIt = false;
568 ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i];
569 for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {
570 ObjCProtocolDecl *PDecl = ConformingProtocols[i1];
571 if (PDecl == TargetPDecl)
572 continue;
573 if (PDecl->lookupProtocolNamed(
574 TargetPDecl->getDeclName().getAsIdentifierInfo())) {
575 DropIt = true;
576 break;
577 }
578 }
579 if (!DropIt)
580 MinimalConformingProtocols.push_back(TargetPDecl);
581 }
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000582 edit::Commit commit(*Editor);
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000583 rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols,
584 *NSAPIObj, commit);
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000585 Editor->commit(commit);
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000586}
587
Fariborz Jahanian92463272013-07-18 20:11:45 +0000588void ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx,
589 const EnumDecl *EnumDcl,
590 const TypedefDecl *TypedefDcl) {
591 if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() ||
592 !TypedefDcl->getIdentifier())
593 return;
594
595 QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000596 bool IsNSIntegerType = NSAPIObj->isObjCNSIntegerType(qt);
597 bool IsNSUIntegerType = !IsNSIntegerType && NSAPIObj->isObjCNSUIntegerType(qt);
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000598
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000599 if (!IsNSIntegerType && !IsNSUIntegerType) {
600 // Also check for typedef enum {...} TD;
601 if (const EnumType *EnumTy = qt->getAs<EnumType>()) {
602 if (EnumTy->getDecl() == EnumDcl) {
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000603 bool NSOptions = UseNSOptionsMacro(Ctx, EnumDcl);
604 if (NSOptions) {
605 if (!Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
606 return;
607 }
608 else if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000609 return;
610 edit::Commit commit(*Editor);
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000611 rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000612 Editor->commit(commit);
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000613 }
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000614 }
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000615 return;
616 }
617 if (IsNSIntegerType && UseNSOptionsMacro(Ctx, EnumDcl)) {
618 // We may still use NS_OPTIONS based on what we find in the enumertor list.
619 IsNSIntegerType = false;
620 IsNSUIntegerType = true;
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000621 }
Fariborz Jahanian92463272013-07-18 20:11:45 +0000622
623 // NS_ENUM must be available.
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000624 if (IsNSIntegerType && !Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
625 return;
626 // NS_OPTIONS must be available.
627 if (IsNSUIntegerType && !Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
Fariborz Jahanian92463272013-07-18 20:11:45 +0000628 return;
629 edit::Commit commit(*Editor);
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000630 rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, IsNSIntegerType);
Fariborz Jahanian92463272013-07-18 20:11:45 +0000631 Editor->commit(commit);
632}
633
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000634static void ReplaceWithInstancetype(const ObjCMigrateASTConsumer &ASTC,
635 ObjCMethodDecl *OM) {
636 SourceRange R;
637 std::string ClassString;
638 if (TypeSourceInfo *TSInfo = OM->getResultTypeSourceInfo()) {
639 TypeLoc TL = TSInfo->getTypeLoc();
640 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc());
641 ClassString = "instancetype";
642 }
643 else {
644 R = SourceRange(OM->getLocStart(), OM->getLocStart());
645 ClassString = OM->isInstanceMethod() ? '-' : '+';
646 ClassString += " (instancetype)";
647 }
648 edit::Commit commit(*ASTC.Editor);
649 commit.replace(R, ClassString);
650 ASTC.Editor->commit(commit);
651}
652
Fariborz Jahanian670ef262013-07-23 23:34:42 +0000653void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx,
654 ObjCContainerDecl *CDecl,
655 ObjCMethodDecl *OM) {
Fariborz Jahanian71221352013-07-23 22:42:28 +0000656 ObjCInstanceTypeFamily OIT_Family =
657 Selector::getInstTypeMethodFamily(OM->getSelector());
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000658
Fariborz Jahanian267bae32013-07-24 19:18:37 +0000659 std::string ClassName;
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000660 switch (OIT_Family) {
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000661 case OIT_None:
662 migrateFactoryMethod(Ctx, CDecl, OM);
663 return;
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000664 case OIT_Array:
Fariborz Jahanian267bae32013-07-24 19:18:37 +0000665 ClassName = "NSArray";
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000666 break;
667 case OIT_Dictionary:
Fariborz Jahanian267bae32013-07-24 19:18:37 +0000668 ClassName = "NSDictionary";
669 break;
670 case OIT_MemManage:
671 ClassName = "NSObject";
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000672 break;
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000673 case OIT_Singleton:
674 migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton);
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000675 return;
676 }
Fariborz Jahanian71221352013-07-23 22:42:28 +0000677 if (!OM->getResultType()->isObjCIdType())
678 return;
679
680 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
681 if (!IDecl) {
682 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
683 IDecl = CatDecl->getClassInterface();
684 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
685 IDecl = ImpDecl->getClassInterface();
686 }
Fariborz Jahanian267bae32013-07-24 19:18:37 +0000687 if (!IDecl ||
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000688 !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) {
689 migrateFactoryMethod(Ctx, CDecl, OM);
Fariborz Jahanian71221352013-07-23 22:42:28 +0000690 return;
Fariborz Jahanian280954a2013-07-24 18:31:42 +0000691 }
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000692 ReplaceWithInstancetype(*this, OM);
Fariborz Jahanian71221352013-07-23 22:42:28 +0000693}
694
695void ObjCMigrateASTConsumer::migrateInstanceType(ASTContext &Ctx,
696 ObjCContainerDecl *CDecl) {
697 // migrate methods which can have instancetype as their result type.
698 for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(),
699 MEnd = CDecl->meth_end();
700 M != MEnd; ++M) {
701 ObjCMethodDecl *Method = (*M);
702 migrateMethodInstanceType(Ctx, CDecl, Method);
703 }
704}
705
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000706void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx,
707 ObjCContainerDecl *CDecl,
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000708 ObjCMethodDecl *OM,
709 ObjCInstanceTypeFamily OIT_Family) {
Fariborz Jahanian3bfc35e2013-08-02 22:34:18 +0000710 if (OM->isInstanceMethod() ||
711 OM->getResultType() == Ctx.getObjCInstanceType() ||
712 !OM->getResultType()->isObjCIdType())
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000713 return;
714
715 // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class
716 // NSYYYNamE with matching names be at least 3 characters long.
717 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
718 if (!IDecl) {
719 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
720 IDecl = CatDecl->getClassInterface();
721 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
722 IDecl = ImpDecl->getClassInterface();
723 }
724 if (!IDecl)
725 return;
726
727 std::string StringClassName = IDecl->getName();
728 StringRef LoweredClassName(StringClassName);
729 std::string StringLoweredClassName = LoweredClassName.lower();
730 LoweredClassName = StringLoweredClassName;
731
732 IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0);
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +0000733 // Handle method with no name at its first selector slot; e.g. + (id):(int)x.
734 if (!MethodIdName)
735 return;
736
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000737 std::string MethodName = MethodIdName->getName();
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000738 if (OIT_Family == OIT_Singleton) {
739 StringRef STRefMethodName(MethodName);
740 size_t len = 0;
741 if (STRefMethodName.startswith("standard"))
742 len = strlen("standard");
743 else if (STRefMethodName.startswith("shared"))
744 len = strlen("shared");
745 else if (STRefMethodName.startswith("default"))
746 len = strlen("default");
747 else
748 return;
749 MethodName = STRefMethodName.substr(len);
750 }
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000751 std::string MethodNameSubStr = MethodName.substr(0, 3);
752 StringRef MethodNamePrefix(MethodNameSubStr);
753 std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower();
754 MethodNamePrefix = StringLoweredMethodNamePrefix;
755 size_t Ix = LoweredClassName.rfind(MethodNamePrefix);
756 if (Ix == StringRef::npos)
757 return;
758 std::string ClassNamePostfix = LoweredClassName.substr(Ix);
759 StringRef LoweredMethodName(MethodName);
760 std::string StringLoweredMethodName = LoweredMethodName.lower();
761 LoweredMethodName = StringLoweredMethodName;
762 if (!LoweredMethodName.startswith(ClassNamePostfix))
763 return;
764 ReplaceWithInstancetype(*this, OM);
765}
766
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000767static bool IsVoidStarType(QualType Ty) {
768 if (!Ty->isPointerType())
769 return false;
770
771 while (const TypedefType *TD = dyn_cast<TypedefType>(Ty.getTypePtr()))
772 Ty = TD->getDecl()->getUnderlyingType();
773
774 // Is the type void*?
775 const PointerType* PT = Ty->getAs<PointerType>();
776 if (PT->getPointeeType().getUnqualifiedType()->isVoidType())
777 return true;
778 return IsVoidStarType(PT->getPointeeType());
779}
780
Fariborz Jahanian94279392013-08-20 18:54:39 +0000781/// AuditedType - This routine audits the type AT and returns false if it is one of known
782/// CF object types or of the "void *" variety. It returns true if we don't care about the type
783/// such as a non-pointer or pointers which have no ownership issues (such as "int *").
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000784static bool
Fariborz Jahanian94279392013-08-20 18:54:39 +0000785AuditedType (QualType AT, bool &IsPoniter) {
786 IsPoniter = (AT->isAnyPointerType() || AT->isBlockPointerType());
787 if (!IsPoniter)
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +0000788 return true;
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +0000789 // FIXME. There isn't much we can say about CF pointer type; or is there?
Fariborz Jahanian94279392013-08-20 18:54:39 +0000790 if (ento::coreFoundation::isCFObjectRef(AT) ||
791 IsVoidStarType(AT) ||
792 // If an ObjC object is type, assuming that it is not a CF function and
793 // that it is an un-audited function.
794 AT->isObjCObjectPointerType())
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +0000795 return false;
Fariborz Jahanian94279392013-08-20 18:54:39 +0000796 // All other pointers are assumed audited as harmless.
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +0000797 return true;
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000798}
799
Fariborz Jahanian301b5212013-08-20 22:42:13 +0000800void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) {
801 if (!Ctx.Idents.get("CF_IMPLICIT_BRIDGING_ENABLED").hasMacroDefinition()) {
802 CFFunctionIBCandidates.clear();
Fariborz Jahanianb8343522013-08-20 23:35:26 +0000803 FileId = 0;
Fariborz Jahanian301b5212013-08-20 22:42:13 +0000804 return;
805 }
806 // Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED
807 const FunctionDecl *FirstFD = CFFunctionIBCandidates[0];
808 const FunctionDecl *LastFD =
809 CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1];
810 const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n";
811 edit::Commit commit(*Editor);
812 commit.insertBefore(FirstFD->getLocStart(), PragmaString);
813 PragmaString = "\n\nCF_IMPLICIT_BRIDGING_DISABLED\n";
814 SourceLocation EndLoc = LastFD->getLocEnd();
815 // get location just past end of function location.
816 EndLoc = PP.getLocForEndOfToken(EndLoc);
817 Token Tok;
818 // get locaiton of token that comes after end of function.
819 bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true);
820 if (!Failed)
821 EndLoc = Tok.getLocation();
822 commit.insertAfterToken(EndLoc, PragmaString);
823 Editor->commit(commit);
Fariborz Jahanianb8343522013-08-20 23:35:26 +0000824 FileId = 0;
Fariborz Jahanian301b5212013-08-20 22:42:13 +0000825 CFFunctionIBCandidates.clear();
826}
827
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000828void ObjCMigrateASTConsumer::migrateCFFunctions(
829 ASTContext &Ctx,
830 const FunctionDecl *FuncDecl) {
Fariborz Jahanianc6dfd3f2013-08-19 22:00:50 +0000831 if (FuncDecl->hasAttr<CFAuditedTransferAttr>()) {
832 assert(CFFunctionIBCandidates.empty() &&
833 "Cannot have audited functions inside user "
834 "provided CF_IMPLICIT_BRIDGING_ENABLE");
835 return;
836 }
837
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000838 // Finction must be annotated first.
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +0000839 bool Audited = migrateAddFunctionAnnotation(Ctx, FuncDecl);
Fariborz Jahanianb8343522013-08-20 23:35:26 +0000840 if (Audited) {
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000841 CFFunctionIBCandidates.push_back(FuncDecl);
Fariborz Jahanianb8343522013-08-20 23:35:26 +0000842 if (!FileId)
843 FileId = PP.getSourceManager().getFileID(FuncDecl->getLocation()).getHashValue();
844 }
Fariborz Jahanian301b5212013-08-20 22:42:13 +0000845 else if (!CFFunctionIBCandidates.empty())
846 AnnotateImplicitBridging(Ctx);
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000847}
848
849bool ObjCMigrateASTConsumer::migrateAddFunctionAnnotation(
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +0000850 ASTContext &Ctx,
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +0000851 const FunctionDecl *FuncDecl) {
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000852 if (FuncDecl->hasBody())
853 return false;
Fariborz Jahanian5874e6d2013-08-20 20:45:28 +0000854
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +0000855 CallEffects CE = CallEffects::getEffect(FuncDecl);
Fariborz Jahanian5874e6d2013-08-20 20:45:28 +0000856 bool FuncIsReturnAnnotated = (FuncDecl->getAttr<CFReturnsRetainedAttr>() ||
857 FuncDecl->getAttr<CFReturnsNotRetainedAttr>());
858
859 // Trivial case of when funciton is annotated and has no argument.
860 if (FuncIsReturnAnnotated && FuncDecl->getNumParams() == 0)
861 return false;
862
863 bool HasAtLeastOnePointer = FuncIsReturnAnnotated;
864 if (!FuncIsReturnAnnotated) {
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +0000865 RetEffect Ret = CE.getReturnValue();
866 const char *AnnotationString = 0;
867 if (Ret.getObjKind() == RetEffect::CF && Ret.isOwned()) {
868 if (Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition())
869 AnnotationString = " CF_RETURNS_RETAINED";
870 }
871 else if (Ret.getObjKind() == RetEffect::CF && !Ret.isOwned()) {
872 if (Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition())
873 AnnotationString = " CF_RETURNS_NOT_RETAINED";
874 }
875 if (AnnotationString) {
876 edit::Commit commit(*Editor);
877 commit.insertAfterToken(FuncDecl->getLocEnd(), AnnotationString);
878 Editor->commit(commit);
Fariborz Jahanian94279392013-08-20 18:54:39 +0000879 HasAtLeastOnePointer = true;
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +0000880 }
Fariborz Jahanian94279392013-08-20 18:54:39 +0000881 else if (!AuditedType(FuncDecl->getResultType(), HasAtLeastOnePointer))
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000882 return false;
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +0000883 }
Fariborz Jahanian94279392013-08-20 18:54:39 +0000884
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +0000885 // At this point result type is either annotated or audited.
886 // Now, how about argument types.
887 llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs();
888 unsigned i = 0;
889 for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),
890 pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {
891 ArgEffect AE = AEArgs[i];
Fariborz Jahanian94279392013-08-20 18:54:39 +0000892 if (AE == DecRef /*CFConsumed annotated*/ ||
893 AE == IncRef) {
894 HasAtLeastOnePointer = true;
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +0000895 continue;
Fariborz Jahanian94279392013-08-20 18:54:39 +0000896 }
Fariborz Jahanianc6dfd3f2013-08-19 22:00:50 +0000897
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +0000898 const ParmVarDecl *pd = *pi;
899 QualType AT = pd->getType();
Fariborz Jahanian94279392013-08-20 18:54:39 +0000900 bool IsPointer;
901 if (!AuditedType(AT, IsPointer))
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +0000902 return false;
Fariborz Jahanian94279392013-08-20 18:54:39 +0000903 else if (IsPointer)
904 HasAtLeastOnePointer = true;
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +0000905 }
Fariborz Jahanian94279392013-08-20 18:54:39 +0000906 return HasAtLeastOnePointer;
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +0000907}
908
909void ObjCMigrateASTConsumer::migrateObjCMethodDeclAnnotation(
910 ASTContext &Ctx,
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +0000911 const ObjCMethodDecl *MethodDecl) {
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +0000912 if (MethodDecl->hasAttr<CFAuditedTransferAttr>() ||
913 MethodDecl->getAttr<CFReturnsRetainedAttr>() ||
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +0000914 MethodDecl->getAttr<CFReturnsNotRetainedAttr>() ||
915 MethodDecl->hasBody())
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +0000916 return;
917}
918
Ted Kremenekf7639e12012-03-06 20:06:33 +0000919namespace {
920
921class RewritesReceiver : public edit::EditsReceiver {
922 Rewriter &Rewrite;
923
924public:
925 RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
926
927 virtual void insert(SourceLocation loc, StringRef text) {
928 Rewrite.InsertText(loc, text);
929 }
930 virtual void replace(CharSourceRange range, StringRef text) {
931 Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
932 }
933};
934
935}
936
937void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000938
939 TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
Fariborz Jahanian301b5212013-08-20 22:42:13 +0000940 if (MigrateProperty) {
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000941 for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();
942 D != DEnd; ++D) {
Fariborz Jahanianb8343522013-08-20 23:35:26 +0000943 if (unsigned FID =
944 PP.getSourceManager().getFileID((*D)->getLocation()).getHashValue())
945 if (FileId && FileId != FID) {
946 assert(!CFFunctionIBCandidates.empty());
947 AnnotateImplicitBridging(Ctx);
948 }
949
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000950 if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))
951 migrateObjCInterfaceDecl(Ctx, CDecl);
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000952 else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D))
953 ObjCProtocolDecls.insert(PDecl);
954 else if (const ObjCImplementationDecl *ImpDecl =
955 dyn_cast<ObjCImplementationDecl>(*D))
956 migrateProtocolConformance(Ctx, ImpDecl);
Fariborz Jahanian92463272013-07-18 20:11:45 +0000957 else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {
958 DeclContext::decl_iterator N = D;
959 ++N;
Fariborz Jahanian008ef722013-07-19 17:44:32 +0000960 if (N != DEnd)
961 if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N))
962 migrateNSEnumDecl(Ctx, ED, TD);
Fariborz Jahanian92463272013-07-18 20:11:45 +0000963 }
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +0000964 else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D))
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +0000965 migrateCFFunctions(Ctx, FD);
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +0000966 else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(*D))
967 migrateObjCMethodDeclAnnotation(Ctx, MD);
968
Fariborz Jahanian71221352013-07-23 22:42:28 +0000969 // migrate methods which can have instancetype as their result type.
970 if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D))
971 migrateInstanceType(Ctx, CDecl);
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000972 }
Fariborz Jahanian301b5212013-08-20 22:42:13 +0000973 if (!CFFunctionIBCandidates.empty())
974 AnnotateImplicitBridging(Ctx);
975 }
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000976
David Blaikiebbafb8a2012-03-11 07:00:24 +0000977 Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000978 RewritesReceiver Rec(rewriter);
979 Editor->applyRewrites(Rec);
980
981 for (Rewriter::buffer_iterator
982 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
983 FileID FID = I->first;
984 RewriteBuffer &buf = I->second;
985 const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
986 assert(file);
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000987 SmallString<512> newText;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000988 llvm::raw_svector_ostream vecOS(newText);
989 buf.write(vecOS);
990 vecOS.flush();
991 llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
992 StringRef(newText.data(), newText.size()), file->getName());
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000993 SmallString<64> filePath(file->getName());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000994 FileMgr.FixupRelativePath(filePath);
995 Remapper.remap(filePath.str(), memBuf);
996 }
997
998 if (IsOutputFile) {
999 Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
1000 } else {
1001 Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
1002 }
1003}
1004
1005bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
Argyrios Kyrtzidisb4822602012-05-24 16:48:23 +00001006 CI.getDiagnostics().setIgnoreAllWarnings(true);
Ted Kremenekf7639e12012-03-06 20:06:33 +00001007 return true;
1008}
1009
1010ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI,
1011 StringRef InFile) {
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +00001012 PPConditionalDirectiveRecord *
1013 PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
1014 CI.getPreprocessor().addPPCallbacks(PPRec);
Ted Kremenekf7639e12012-03-06 20:06:33 +00001015 return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile,
1016 /*MigrateLiterals=*/true,
1017 /*MigrateSubscripting=*/true,
Fariborz Jahaniand83ef842013-07-09 16:59:14 +00001018 /*MigrateProperty*/true,
Ted Kremenekf7639e12012-03-06 20:06:33 +00001019 Remapper,
1020 CI.getFileManager(),
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +00001021 PPRec,
Fariborz Jahaniana7437f02013-07-03 23:05:00 +00001022 CI.getPreprocessor(),
Ted Kremenekf7639e12012-03-06 20:06:33 +00001023 /*isOutputFile=*/true);
1024}