blob: 332fc96ef5d6b2efd4c1bab1390ae2638e830d66 [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 {
Fariborz Jahanian63ffce22013-08-27 22:42:30 +000039 enum CF_BRIDGING_KIND {
40 CF_BRIDGING_NONE,
41 CF_BRIDGING_ENABLE,
42 CF_BRIDGING_MAY_INCLUDE
43 };
44
Ted Kremenekf7639e12012-03-06 20:06:33 +000045 void migrateDecl(Decl *D);
Fariborz Jahanian92f72422013-09-17 19:00:30 +000046 void migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCContainerDecl *D);
Fariborz Jahaniand41dbad2013-10-31 16:10:44 +000047 void migrateDeprecatedAnnotation(ASTContext &Ctx, ObjCCategoryDecl *CatDecl);
Fariborz Jahanian1be01532013-07-12 22:32:19 +000048 void migrateProtocolConformance(ASTContext &Ctx,
49 const ObjCImplementationDecl *ImpDecl);
Fariborz Jahanian059e05e2013-10-15 00:00:28 +000050 void CacheObjCNSIntegerTypedefed(const TypedefDecl *TypedefDcl);
Fariborz Jahanian11dd4b12013-10-11 21:34:56 +000051 bool migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl,
Fariborz Jahanian92463272013-07-18 20:11:45 +000052 const TypedefDecl *TypedefDcl);
Fariborz Jahanian8c45e282013-10-02 22:49:59 +000053 void migrateAllMethodInstaceType(ASTContext &Ctx, ObjCContainerDecl *CDecl);
Fariborz Jahanian670ef262013-07-23 23:34:42 +000054 void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl,
55 ObjCMethodDecl *OM);
Fariborz Jahanian92f72422013-09-17 19:00:30 +000056 bool migrateProperty(ASTContext &Ctx, ObjCContainerDecl *D, ObjCMethodDecl *OM);
Fariborz Jahaniand0fbf6c2013-08-30 23:52:08 +000057 void migrateNsReturnsInnerPointer(ASTContext &Ctx, ObjCMethodDecl *OM);
Fariborz Jahanian10b74352013-09-24 20:20:52 +000058 void migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, ObjCPropertyDecl *P);
Fariborz Jahanianc4291852013-08-02 18:00:53 +000059 void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl,
Fariborz Jahanian9275c682013-08-02 20:54:18 +000060 ObjCMethodDecl *OM,
61 ObjCInstanceTypeFamily OIT_Family = OIT_None);
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +000062
Fariborz Jahanian4f64dd22013-08-22 21:40:15 +000063 void migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl);
Fariborz Jahanian63ffce22013-08-27 22:42:30 +000064 void AddCFAnnotations(ASTContext &Ctx, const CallEffects &CE,
Fariborz Jahanian7ca1d302013-08-27 23:56:54 +000065 const FunctionDecl *FuncDecl, bool ResultAnnotated);
Fariborz Jahanian63ffce22013-08-27 22:42:30 +000066 void AddCFAnnotations(ASTContext &Ctx, const CallEffects &CE,
Fariborz Jahanian7ca1d302013-08-27 23:56:54 +000067 const ObjCMethodDecl *MethodDecl, bool ResultAnnotated);
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +000068
Fariborz Jahanian301b5212013-08-20 22:42:13 +000069 void AnnotateImplicitBridging(ASTContext &Ctx);
70
Fariborz Jahanian63ffce22013-08-27 22:42:30 +000071 CF_BRIDGING_KIND migrateAddFunctionAnnotation(ASTContext &Ctx,
72 const FunctionDecl *FuncDecl);
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +000073
Fariborz Jahanian926fafb2013-08-22 18:35:27 +000074 void migrateARCSafeAnnotation(ASTContext &Ctx, ObjCContainerDecl *CDecl);
75
Fariborz Jahanian89f6d102013-09-04 00:10:06 +000076 void migrateAddMethodAnnotation(ASTContext &Ctx,
77 const ObjCMethodDecl *MethodDecl);
Ted Kremenekf7639e12012-03-06 20:06:33 +000078public:
79 std::string MigrateDir;
Fariborz Jahanian182486c2013-10-02 17:08:12 +000080 unsigned ASTMigrateActions;
Fariborz Jahanianb8343522013-08-20 23:35:26 +000081 unsigned FileId;
Fariborz Jahanian059e05e2013-10-15 00:00:28 +000082 const TypedefDecl *NSIntegerTypedefed;
83 const TypedefDecl *NSUIntegerTypedefed;
Dmitri Gribenkof8579502013-01-12 19:30:44 +000084 OwningPtr<NSAPI> NSAPIObj;
85 OwningPtr<edit::EditedSource> Editor;
Ted Kremenekf7639e12012-03-06 20:06:33 +000086 FileRemapper &Remapper;
87 FileManager &FileMgr;
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +000088 const PPConditionalDirectiveRecord *PPRec;
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000089 Preprocessor &PP;
Ted Kremenekf7639e12012-03-06 20:06:33 +000090 bool IsOutputFile;
Fariborz Jahanian1be01532013-07-12 22:32:19 +000091 llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls;
Fariborz Jahanian926fafb2013-08-22 18:35:27 +000092 llvm::SmallVector<const Decl *, 8> CFFunctionIBCandidates;
Fariborz Jahanian1be01532013-07-12 22:32:19 +000093
Ted Kremenekf7639e12012-03-06 20:06:33 +000094 ObjCMigrateASTConsumer(StringRef migrateDir,
Fariborz Jahanian182486c2013-10-02 17:08:12 +000095 unsigned astMigrateActions,
Ted Kremenekf7639e12012-03-06 20:06:33 +000096 FileRemapper &remapper,
97 FileManager &fileMgr,
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +000098 const PPConditionalDirectiveRecord *PPRec,
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000099 Preprocessor &PP,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000100 bool isOutputFile = false)
101 : MigrateDir(migrateDir),
Fariborz Jahanian182486c2013-10-02 17:08:12 +0000102 ASTMigrateActions(astMigrateActions),
Fariborz Jahanian059e05e2013-10-15 00:00:28 +0000103 FileId(0), NSIntegerTypedefed(0), NSUIntegerTypedefed(0),
104 Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),
Ted Kremenekf7639e12012-03-06 20:06:33 +0000105 IsOutputFile(isOutputFile) { }
106
107protected:
108 virtual void Initialize(ASTContext &Context) {
109 NSAPIObj.reset(new NSAPI(Context));
110 Editor.reset(new edit::EditedSource(Context.getSourceManager(),
David Blaikiebbafb8a2012-03-11 07:00:24 +0000111 Context.getLangOpts(),
Fariborz Jahanian8f5225b2013-10-01 21:16:29 +0000112 PPRec, false));
Ted Kremenekf7639e12012-03-06 20:06:33 +0000113 }
114
115 virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
116 for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
117 migrateDecl(*I);
118 return true;
119 }
120 virtual void HandleInterestingDecl(DeclGroupRef DG) {
121 // Ignore decls from the PCH.
122 }
123 virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
124 ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
125 }
126
127 virtual void HandleTranslationUnit(ASTContext &Ctx);
128};
129
130}
131
132ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction,
Fariborz Jahanian182486c2013-10-02 17:08:12 +0000133 StringRef migrateDir,
134 unsigned migrateAction)
Ted Kremenekf7639e12012-03-06 20:06:33 +0000135 : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir),
Fariborz Jahanian182486c2013-10-02 17:08:12 +0000136 ObjCMigAction(migrateAction),
Ted Kremenekf7639e12012-03-06 20:06:33 +0000137 CompInst(0) {
138 if (MigrateDir.empty())
139 MigrateDir = "."; // user current directory if none is given.
140}
141
142ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI,
143 StringRef InFile) {
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +0000144 PPConditionalDirectiveRecord *
145 PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
146 CompInst->getPreprocessor().addPPCallbacks(PPRec);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000147 ASTConsumer *
148 WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
149 ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir,
Fariborz Jahanian182486c2013-10-02 17:08:12 +0000150 ObjCMigAction,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000151 Remapper,
152 CompInst->getFileManager(),
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000153 PPRec,
154 CompInst->getPreprocessor());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000155 ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer };
156 return new MultiplexConsumer(Consumers);
157}
158
159bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
160 Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
161 /*ignoreIfFilesChanges=*/true);
162 CompInst = &CI;
163 CI.getDiagnostics().setIgnoreAllWarnings(true);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000164 return true;
165}
166
167namespace {
168class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
169 ObjCMigrateASTConsumer &Consumer;
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000170 ParentMap &PMap;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000171
172public:
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000173 ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
174 : Consumer(consumer), PMap(PMap) { }
Ted Kremenekf7639e12012-03-06 20:06:33 +0000175
176 bool shouldVisitTemplateInstantiations() const { return false; }
177 bool shouldWalkTypesOfTypeLocs() const { return false; }
178
179 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
Fariborz Jahanian182486c2013-10-02 17:08:12 +0000180 if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Literals) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000181 edit::Commit commit(*Consumer.Editor);
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000182 edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000183 Consumer.Editor->commit(commit);
184 }
185
Fariborz Jahanian182486c2013-10-02 17:08:12 +0000186 if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Subscripting) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000187 edit::Commit commit(*Consumer.Editor);
188 edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
189 Consumer.Editor->commit(commit);
190 }
191
192 return true;
193 }
194
195 bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
196 // Do depth first; we want to rewrite the subexpressions first so that if
197 // we have to move expressions we will move them already rewritten.
198 for (Stmt::child_range range = E->children(); range; ++range)
199 if (!TraverseStmt(*range))
200 return false;
201
202 return WalkUpFromObjCMessageExpr(E);
203 }
204};
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000205
206class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
207 ObjCMigrateASTConsumer &Consumer;
208 OwningPtr<ParentMap> PMap;
209
210public:
211 BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
212
213 bool shouldVisitTemplateInstantiations() const { return false; }
214 bool shouldWalkTypesOfTypeLocs() const { return false; }
215
216 bool TraverseStmt(Stmt *S) {
217 PMap.reset(new ParentMap(S));
218 ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
219 return true;
220 }
221};
Ted Kremenekf7639e12012-03-06 20:06:33 +0000222}
223
224void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
225 if (!D)
226 return;
227 if (isa<ObjCMethodDecl>(D))
228 return; // Wait for the ObjC container declaration.
229
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000230 BodyMigrator(*this).TraverseDecl(D);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000231}
232
Fariborz Jahanian4a8865b2013-10-16 19:48:23 +0000233static void append_attr(std::string &PropertyString, const char *attr,
234 bool &LParenAdded) {
235 if (!LParenAdded) {
236 PropertyString += "(";
237 LParenAdded = true;
238 }
239 else
240 PropertyString += ", ";
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000241 PropertyString += attr;
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000242}
243
Fariborz Jahanian02461d02013-10-08 20:14:24 +0000244static
245void MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString,
246 const std::string& TypeString,
247 const char *name) {
248 const char *argPtr = TypeString.c_str();
249 int paren = 0;
250 while (*argPtr) {
251 switch (*argPtr) {
252 case '(':
253 PropertyString += *argPtr;
254 paren++;
255 break;
256 case ')':
257 PropertyString += *argPtr;
258 paren--;
259 break;
260 case '^':
Fariborz Jahanian3c593d62013-10-08 21:32:16 +0000261 case '*':
262 PropertyString += (*argPtr);
263 if (paren == 1) {
Fariborz Jahanian02461d02013-10-08 20:14:24 +0000264 PropertyString += name;
Fariborz Jahanian3c593d62013-10-08 21:32:16 +0000265 name = "";
266 }
Fariborz Jahanian02461d02013-10-08 20:14:24 +0000267 break;
268 default:
269 PropertyString += *argPtr;
270 break;
271 }
272 argPtr++;
273 }
274}
275
276
Fariborz Jahanian1b667872013-10-16 22:35:19 +0000277static void rewriteToObjCProperty(const ObjCMethodDecl *Getter,
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000278 const ObjCMethodDecl *Setter,
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000279 const NSAPI &NS, edit::Commit &commit,
Fariborz Jahanian20a11242013-10-09 19:06:08 +0000280 unsigned LengthOfPrefix,
Fariborz Jahanian071b98e2013-11-01 00:26:48 +0000281 bool Atomic, bool AvailabilityArgsMatch) {
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000282 ASTContext &Context = NS.getASTContext();
Fariborz Jahanian4a8865b2013-10-16 19:48:23 +0000283 bool LParenAdded = false;
284 std::string PropertyString = "@property ";
Fariborz Jahanianbed1be92013-11-12 19:25:50 +0000285 if (Context.Idents.get("NS_NONATOMIC_IOSONLY").hasMacroDefinition()) {
286 PropertyString += "(NS_NONATOMIC_IOSONLY";
287 LParenAdded = true;
288 } else if (!Atomic) {
Fariborz Jahanian4a8865b2013-10-16 19:48:23 +0000289 PropertyString += "(nonatomic";
290 LParenAdded = true;
291 }
292
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000293 std::string PropertyNameString = Getter->getNameAsString();
294 StringRef PropertyName(PropertyNameString);
Fariborz Jahanianca399602013-09-11 17:05:15 +0000295 if (LengthOfPrefix > 0) {
Fariborz Jahanian4a8865b2013-10-16 19:48:23 +0000296 if (!LParenAdded) {
297 PropertyString += "(getter=";
298 LParenAdded = true;
299 }
300 else
301 PropertyString += ", getter=";
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000302 PropertyString += PropertyNameString;
303 }
Fariborz Jahanian55d6e6c2013-08-28 23:22:46 +0000304 // Property with no setter may be suggested as a 'readonly' property.
Fariborz Jahanian4a8865b2013-10-16 19:48:23 +0000305 if (!Setter) {
306 if (!LParenAdded) {
307 PropertyString += "(readonly";
308 LParenAdded = true;
309 }
310 else
311 append_attr(PropertyString, "readonly", LParenAdded);
312 }
Fariborz Jahanian02461d02013-10-08 20:14:24 +0000313
Fariborz Jahanian33304e302013-10-16 18:52:17 +0000314 // Short circuit 'delegate' properties that contain the name "delegate" or
315 // "dataSource", or have exact name "target" to have 'assign' attribute.
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000316 if (PropertyName.equals("target") ||
317 (PropertyName.find("delegate") != StringRef::npos) ||
Fariborz Jahanian78bff052013-10-16 20:44:26 +0000318 (PropertyName.find("dataSource") != StringRef::npos)) {
319 QualType QT = Getter->getResultType();
320 if (!QT->isRealType())
321 append_attr(PropertyString, "assign", LParenAdded);
322 }
Fariborz Jahanian55d6e6c2013-08-28 23:22:46 +0000323 else if (Setter) {
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000324 const ParmVarDecl *argDecl = *Setter->param_begin();
325 QualType ArgType = Context.getCanonicalType(argDecl->getType());
326 Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();
327 bool RetainableObject = ArgType->isObjCRetainableType();
328 if (RetainableObject && propertyLifetime == Qualifiers::OCL_Strong) {
329 if (const ObjCObjectPointerType *ObjPtrTy =
330 ArgType->getAs<ObjCObjectPointerType>()) {
331 ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
332 if (IDecl &&
333 IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying")))
Fariborz Jahanian4a8865b2013-10-16 19:48:23 +0000334 append_attr(PropertyString, "copy", LParenAdded);
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000335 else
Fariborz Jahanian4a8865b2013-10-16 19:48:23 +0000336 append_attr(PropertyString, "retain", LParenAdded);
Fariborz Jahanian447b15e2013-08-21 18:49:03 +0000337 }
Fariborz Jahanian02461d02013-10-08 20:14:24 +0000338 else if (ArgType->isBlockPointerType())
Fariborz Jahanian4a8865b2013-10-16 19:48:23 +0000339 append_attr(PropertyString, "copy", LParenAdded);
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000340 } else if (propertyLifetime == Qualifiers::OCL_Weak)
341 // TODO. More precise determination of 'weak' attribute requires
342 // looking into setter's implementation for backing weak ivar.
Fariborz Jahanian4a8865b2013-10-16 19:48:23 +0000343 append_attr(PropertyString, "weak", LParenAdded);
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000344 else if (RetainableObject)
Fariborz Jahanian02461d02013-10-08 20:14:24 +0000345 append_attr(PropertyString,
Fariborz Jahanian4a8865b2013-10-16 19:48:23 +0000346 ArgType->isBlockPointerType() ? "copy" : "retain", LParenAdded);
Fariborz Jahaniancf387c62013-08-06 18:06:23 +0000347 }
Fariborz Jahanian4a8865b2013-10-16 19:48:23 +0000348 if (LParenAdded)
349 PropertyString += ')';
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000350 QualType RT = Getter->getResultType();
351 if (!isa<TypedefType>(RT)) {
352 // strip off any ARC lifetime qualifier.
353 QualType CanResultTy = Context.getCanonicalType(RT);
354 if (CanResultTy.getQualifiers().hasObjCLifetime()) {
355 Qualifiers Qs = CanResultTy.getQualifiers();
356 Qs.removeObjCLifetime();
357 RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs);
358 }
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000359 }
360 PropertyString += " ";
Fariborz Jahanian02461d02013-10-08 20:14:24 +0000361 PrintingPolicy SubPolicy(Context.getPrintingPolicy());
362 SubPolicy.SuppressStrongLifetime = true;
Fariborz Jahaniande557f82013-10-09 17:37:28 +0000363 SubPolicy.SuppressLifetimeQualifiers = true;
Fariborz Jahanian02461d02013-10-08 20:14:24 +0000364 std::string TypeString = RT.getAsString(SubPolicy);
Fariborz Jahanianca399602013-09-11 17:05:15 +0000365 if (LengthOfPrefix > 0) {
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000366 // property name must strip off "is" and lower case the first character
367 // after that; e.g. isContinuous will become continuous.
368 StringRef PropertyNameStringRef(PropertyNameString);
Fariborz Jahanianca399602013-09-11 17:05:15 +0000369 PropertyNameStringRef = PropertyNameStringRef.drop_front(LengthOfPrefix);
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000370 PropertyNameString = PropertyNameStringRef;
Fariborz Jahanian02461d02013-10-08 20:14:24 +0000371 bool NoLowering = (isUppercase(PropertyNameString[0]) &&
372 PropertyNameString.size() > 1 &&
373 isUppercase(PropertyNameString[1]));
Fariborz Jahanian34fea362013-09-11 18:27:16 +0000374 if (!NoLowering)
Fariborz Jahanian02461d02013-10-08 20:14:24 +0000375 PropertyNameString[0] = toLowercase(PropertyNameString[0]);
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000376 }
Fariborz Jahanian02461d02013-10-08 20:14:24 +0000377 if (RT->isBlockPointerType() || RT->isFunctionPointerType())
378 MigrateBlockOrFunctionPointerTypeVariable(PropertyString,
379 TypeString,
380 PropertyNameString.c_str());
381 else {
382 char LastChar = TypeString[TypeString.size()-1];
383 PropertyString += TypeString;
384 if (LastChar != '*')
385 PropertyString += ' ';
Fariborz Jahaniancf2ff9b2013-08-08 20:51:58 +0000386 PropertyString += PropertyNameString;
Fariborz Jahanian02461d02013-10-08 20:14:24 +0000387 }
Fariborz Jahanian215f96c2013-09-06 23:45:20 +0000388 SourceLocation StartGetterSelectorLoc = Getter->getSelectorStartLoc();
389 Selector GetterSelector = Getter->getSelector();
390
391 SourceLocation EndGetterSelectorLoc =
392 StartGetterSelectorLoc.getLocWithOffset(GetterSelector.getNameForSlot(0).size());
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000393 commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(),
Fariborz Jahanian215f96c2013-09-06 23:45:20 +0000394 EndGetterSelectorLoc),
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000395 PropertyString);
Fariborz Jahanian071b98e2013-11-01 00:26:48 +0000396 if (Setter && AvailabilityArgsMatch) {
Fariborz Jahanian55d6e6c2013-08-28 23:22:46 +0000397 SourceLocation EndLoc = Setter->getDeclaratorEndLoc();
398 // Get location past ';'
399 EndLoc = EndLoc.getLocWithOffset(1);
Fariborz Jahanian1b667872013-10-16 22:35:19 +0000400 SourceLocation BeginOfSetterDclLoc = Setter->getLocStart();
401 // FIXME. This assumes that setter decl; is immediately preceeded by eoln.
402 // It is trying to remove the setter method decl. line entirely.
403 BeginOfSetterDclLoc = BeginOfSetterDclLoc.getLocWithOffset(-1);
404 commit.remove(SourceRange(BeginOfSetterDclLoc, EndLoc));
Fariborz Jahanian55d6e6c2013-08-28 23:22:46 +0000405 }
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000406}
407
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000408void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx,
Fariborz Jahanian92f72422013-09-17 19:00:30 +0000409 ObjCContainerDecl *D) {
Fariborz Jahanian75226d52013-09-17 21:56:04 +0000410 if (D->isDeprecated())
411 return;
412
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000413 for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end();
414 M != MEnd; ++M) {
415 ObjCMethodDecl *Method = (*M);
Fariborz Jahanian75226d52013-09-17 21:56:04 +0000416 if (Method->isDeprecated())
417 continue;
Fariborz Jahanianec7cea92013-11-08 01:15:17 +0000418 bool PropertyInferred = migrateProperty(Ctx, D, Method);
419 // If a property is inferred, do not attempt to attach NS_RETURNS_INNER_POINTER to
420 // the getter method as it ends up on the property itself which we don't want
421 // to do unless -objcmt-returns-innerpointer-property option is on.
422 if (!PropertyInferred ||
423 (ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty))
Fariborz Jahanian22626e72013-11-08 02:00:22 +0000424 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
425 migrateNsReturnsInnerPointer(Ctx, Method);
Fariborz Jahanian10b74352013-09-24 20:20:52 +0000426 }
Fariborz Jahanian23417072013-11-05 22:28:30 +0000427 if (!(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty))
428 return;
429
Fariborz Jahanian10b74352013-09-24 20:20:52 +0000430 for (ObjCContainerDecl::prop_iterator P = D->prop_begin(),
431 E = D->prop_end(); P != E; ++P) {
432 ObjCPropertyDecl *Prop = *P;
Fariborz Jahanian8c45e282013-10-02 22:49:59 +0000433 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
Fariborz Jahanian9d2ffea2013-10-31 00:06:58 +0000434 !Prop->isDeprecated())
Fariborz Jahanian10b74352013-09-24 20:20:52 +0000435 migratePropertyNsReturnsInnerPointer(Ctx, Prop);
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000436 }
437}
438
Fariborz Jahaniand41dbad2013-10-31 16:10:44 +0000439void ObjCMigrateASTConsumer::migrateDeprecatedAnnotation(ASTContext &Ctx,
Fariborz Jahanian9d2ffea2013-10-31 00:06:58 +0000440 ObjCCategoryDecl *CatDecl) {
441 StringRef Name = CatDecl->getName();
Fariborz Jahaniand41dbad2013-10-31 16:10:44 +0000442 if (!Name.endswith("Deprecated"))
Fariborz Jahanian9d2ffea2013-10-31 00:06:58 +0000443 return;
444
445 if (!Ctx.Idents.get("DEPRECATED").hasMacroDefinition())
446 return;
447
448 ObjCContainerDecl *D = cast<ObjCContainerDecl>(CatDecl);
449
450 for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end();
451 M != MEnd; ++M) {
452 ObjCMethodDecl *Method = (*M);
453 if (Method->isDeprecated() || Method->isImplicit())
454 continue;
455 // Annotate with DEPRECATED
456 edit::Commit commit(*Editor);
457 commit.insertBefore(Method->getLocEnd(), " DEPRECATED");
458 Editor->commit(commit);
459 }
460 for (ObjCContainerDecl::prop_iterator P = D->prop_begin(),
461 E = D->prop_end(); P != E; ++P) {
462 ObjCPropertyDecl *Prop = *P;
463 if (Prop->isDeprecated())
464 continue;
465 // Annotate with DEPRECATED
466 edit::Commit commit(*Editor);
467 commit.insertAfterToken(Prop->getLocEnd(), " DEPRECATED");
468 Editor->commit(commit);
469 }
470}
471
472static bool
Fariborz Jahanian9a3512b2013-07-13 17:16:41 +0000473ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000474 const ObjCImplementationDecl *ImpDecl,
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000475 const ObjCInterfaceDecl *IDecl,
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000476 ObjCProtocolDecl *Protocol) {
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000477 // In auto-synthesis, protocol properties are not synthesized. So,
478 // a conforming protocol must have its required properties declared
479 // in class interface.
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000480 bool HasAtleastOneRequiredProperty = false;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000481 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition())
482 for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(),
483 E = PDecl->prop_end(); P != E; ++P) {
484 ObjCPropertyDecl *Property = *P;
485 if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional)
486 continue;
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000487 HasAtleastOneRequiredProperty = true;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000488 DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName());
Fariborz Jahanian2bc3dda2013-07-16 21:59:42 +0000489 if (R.size() == 0) {
490 // Relax the rule and look into class's implementation for a synthesize
491 // or dynamic declaration. Class is implementing a property coming from
492 // another protocol. This still makes the target protocol as conforming.
493 if (!ImpDecl->FindPropertyImplDecl(
494 Property->getDeclName().getAsIdentifierInfo()))
495 return false;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000496 }
Fariborz Jahanian2bc3dda2013-07-16 21:59:42 +0000497 else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) {
498 if ((ClassProperty->getPropertyAttributes()
499 != Property->getPropertyAttributes()) ||
500 !Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
501 return false;
502 }
503 else
504 return false;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000505 }
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000506
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000507 // At this point, all required properties in this protocol conform to those
508 // declared in the class.
509 // Check that class implements the required methods of the protocol too.
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000510 bool HasAtleastOneRequiredMethod = false;
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000511 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) {
512 if (PDecl->meth_begin() == PDecl->meth_end())
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000513 return HasAtleastOneRequiredProperty;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000514 for (ObjCContainerDecl::method_iterator M = PDecl->meth_begin(),
515 MEnd = PDecl->meth_end(); M != MEnd; ++M) {
516 ObjCMethodDecl *MD = (*M);
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000517 if (MD->isImplicit())
518 continue;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000519 if (MD->getImplementationControl() == ObjCMethodDecl::Optional)
520 continue;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000521 DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName());
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000522 if (R.size() == 0)
523 return false;
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000524 bool match = false;
525 HasAtleastOneRequiredMethod = true;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000526 for (unsigned I = 0, N = R.size(); I != N; ++I)
527 if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0]))
528 if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {
529 match = true;
530 break;
531 }
532 if (!match)
533 return false;
534 }
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000535 }
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000536 if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod)
537 return true;
538 return false;
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000539}
540
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000541static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl,
542 llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols,
543 const NSAPI &NS, edit::Commit &commit) {
544 const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols();
545 std::string ClassString;
546 SourceLocation EndLoc =
547 IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation();
548
549 if (Protocols.empty()) {
550 ClassString = '<';
551 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
552 ClassString += ConformingProtocols[i]->getNameAsString();
553 if (i != (e-1))
554 ClassString += ", ";
555 }
556 ClassString += "> ";
557 }
558 else {
559 ClassString = ", ";
560 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
561 ClassString += ConformingProtocols[i]->getNameAsString();
562 if (i != (e-1))
563 ClassString += ", ";
564 }
565 ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1;
566 EndLoc = *PL;
567 }
568
569 commit.insertAfterToken(EndLoc, ClassString);
570 return true;
571}
572
573static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,
574 const TypedefDecl *TypedefDcl,
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000575 const NSAPI &NS, edit::Commit &commit,
Fariborz Jahanianff3476e2013-08-30 17:46:01 +0000576 bool IsNSIntegerType,
577 bool NSOptions) {
578 std::string ClassString;
579 if (NSOptions)
580 ClassString = "typedef NS_OPTIONS(NSUInteger, ";
581 else
582 ClassString =
583 IsNSIntegerType ? "typedef NS_ENUM(NSInteger, "
584 : "typedef NS_ENUM(NSUInteger, ";
585
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000586 ClassString += TypedefDcl->getIdentifier()->getName();
587 ClassString += ')';
588 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
589 commit.replace(R, ClassString);
Fariborz Jahanian059e05e2013-10-15 00:00:28 +0000590 SourceLocation EndOfEnumDclLoc = EnumDcl->getLocEnd();
591 EndOfEnumDclLoc = trans::findSemiAfterLocation(EndOfEnumDclLoc,
592 NS.getASTContext(), /*IsDecl*/true);
593 if (!EndOfEnumDclLoc.isInvalid()) {
594 SourceRange EnumDclRange(EnumDcl->getLocStart(), EndOfEnumDclLoc);
595 commit.insertFromRange(TypedefDcl->getLocStart(), EnumDclRange);
596 }
597 else
598 return false;
599
600 SourceLocation EndTypedefDclLoc = TypedefDcl->getLocEnd();
601 EndTypedefDclLoc = trans::findSemiAfterLocation(EndTypedefDclLoc,
602 NS.getASTContext(), /*IsDecl*/true);
603 if (!EndTypedefDclLoc.isInvalid()) {
604 SourceRange TDRange(TypedefDcl->getLocStart(), EndTypedefDclLoc);
605 commit.remove(TDRange);
606 }
607 else
608 return false;
609
610 EndOfEnumDclLoc = trans::findLocationAfterSemi(EnumDcl->getLocEnd(), NS.getASTContext(),
Fariborz Jahanian1d27ffd2013-10-11 17:35:22 +0000611 /*IsDecl*/true);
Fariborz Jahanian059e05e2013-10-15 00:00:28 +0000612 if (!EndOfEnumDclLoc.isInvalid()) {
613 SourceLocation BeginOfEnumDclLoc = EnumDcl->getLocStart();
614 // FIXME. This assumes that enum decl; is immediately preceeded by eoln.
615 // It is trying to remove the enum decl. lines entirely.
616 BeginOfEnumDclLoc = BeginOfEnumDclLoc.getLocWithOffset(-1);
617 commit.remove(SourceRange(BeginOfEnumDclLoc, EndOfEnumDclLoc));
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000618 return true;
619 }
620 return false;
621}
622
Fariborz Jahanian403425b2013-10-17 23:13:13 +0000623static void rewriteToNSMacroDecl(const EnumDecl *EnumDcl,
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000624 const TypedefDecl *TypedefDcl,
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000625 const NSAPI &NS, edit::Commit &commit,
626 bool IsNSIntegerType) {
627 std::string ClassString =
628 IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, ";
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000629 ClassString += TypedefDcl->getIdentifier()->getName();
630 ClassString += ')';
631 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
632 commit.replace(R, ClassString);
633 SourceLocation TypedefLoc = TypedefDcl->getLocEnd();
634 commit.remove(SourceRange(TypedefLoc, TypedefLoc));
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000635}
636
Fariborz Jahaniana23f4fb2013-08-30 00:10:37 +0000637static bool UseNSOptionsMacro(Preprocessor &PP, ASTContext &Ctx,
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000638 const EnumDecl *EnumDcl) {
639 bool PowerOfTwo = true;
Fariborz Jahanian02bdb162013-09-23 20:27:06 +0000640 bool AllHexdecimalEnumerator = true;
Fariborz Jahanianbe7bc112013-08-15 18:46:37 +0000641 uint64_t MaxPowerOfTwoVal = 0;
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000642 for (EnumDecl::enumerator_iterator EI = EnumDcl->enumerator_begin(),
643 EE = EnumDcl->enumerator_end(); EI != EE; ++EI) {
644 EnumConstantDecl *Enumerator = (*EI);
645 const Expr *InitExpr = Enumerator->getInitExpr();
646 if (!InitExpr) {
647 PowerOfTwo = false;
Fariborz Jahanian02bdb162013-09-23 20:27:06 +0000648 AllHexdecimalEnumerator = false;
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000649 continue;
650 }
Fariborz Jahanian6e1798e2013-09-17 23:32:51 +0000651 InitExpr = InitExpr->IgnoreParenCasts();
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000652 if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
653 if (BO->isShiftOp() || BO->isBitwiseOp())
654 return true;
655
656 uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
Fariborz Jahanianbe7bc112013-08-15 18:46:37 +0000657 if (PowerOfTwo && EnumVal) {
658 if (!llvm::isPowerOf2_64(EnumVal))
659 PowerOfTwo = false;
660 else if (EnumVal > MaxPowerOfTwoVal)
661 MaxPowerOfTwoVal = EnumVal;
662 }
Fariborz Jahanian02bdb162013-09-23 20:27:06 +0000663 if (AllHexdecimalEnumerator && EnumVal) {
664 bool FoundHexdecimalEnumerator = false;
Fariborz Jahaniana23f4fb2013-08-30 00:10:37 +0000665 SourceLocation EndLoc = Enumerator->getLocEnd();
666 Token Tok;
667 if (!PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true))
668 if (Tok.isLiteral() && Tok.getLength() > 2) {
669 if (const char *StringLit = Tok.getLiteralData())
670 FoundHexdecimalEnumerator =
671 (StringLit[0] == '0' && (toLowercase(StringLit[1]) == 'x'));
672 }
Fariborz Jahanian02bdb162013-09-23 20:27:06 +0000673 if (!FoundHexdecimalEnumerator)
674 AllHexdecimalEnumerator = false;
Fariborz Jahaniana23f4fb2013-08-30 00:10:37 +0000675 }
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000676 }
Fariborz Jahanian02bdb162013-09-23 20:27:06 +0000677 return AllHexdecimalEnumerator || (PowerOfTwo && (MaxPowerOfTwoVal > 2));
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000678}
679
Fariborz Jahanian008ef722013-07-19 17:44:32 +0000680void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000681 const ObjCImplementationDecl *ImpDecl) {
682 const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();
Fariborz Jahanian75226d52013-09-17 21:56:04 +0000683 if (!IDecl || ObjCProtocolDecls.empty() || IDecl->isDeprecated())
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000684 return;
685 // Find all implicit conforming protocols for this class
686 // and make them explicit.
687 llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols;
688 Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols);
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000689 llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols;
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000690
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000691 for (llvm::SmallPtrSet<ObjCProtocolDecl*, 32>::iterator I =
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000692 ObjCProtocolDecls.begin(),
693 E = ObjCProtocolDecls.end(); I != E; ++I)
694 if (!ExplicitProtocols.count(*I))
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000695 PotentialImplicitProtocols.push_back(*I);
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000696
697 if (PotentialImplicitProtocols.empty())
698 return;
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000699
700 // go through list of non-optional methods and properties in each protocol
701 // in the PotentialImplicitProtocols list. If class implements every one of the
702 // methods and properties, then this class conforms to this protocol.
703 llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols;
704 for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000705 if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl,
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000706 PotentialImplicitProtocols[i]))
707 ConformingProtocols.push_back(PotentialImplicitProtocols[i]);
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000708
709 if (ConformingProtocols.empty())
710 return;
Fariborz Jahaniancb7b8de2013-07-17 00:02:22 +0000711
712 // Further reduce number of conforming protocols. If protocol P1 is in the list
713 // protocol P2 (P2<P1>), No need to include P1.
714 llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols;
715 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
716 bool DropIt = false;
717 ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i];
718 for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {
719 ObjCProtocolDecl *PDecl = ConformingProtocols[i1];
720 if (PDecl == TargetPDecl)
721 continue;
722 if (PDecl->lookupProtocolNamed(
723 TargetPDecl->getDeclName().getAsIdentifierInfo())) {
724 DropIt = true;
725 break;
726 }
727 }
728 if (!DropIt)
729 MinimalConformingProtocols.push_back(TargetPDecl);
730 }
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000731 edit::Commit commit(*Editor);
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000732 rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols,
733 *NSAPIObj, commit);
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000734 Editor->commit(commit);
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000735}
736
Fariborz Jahanian059e05e2013-10-15 00:00:28 +0000737void ObjCMigrateASTConsumer::CacheObjCNSIntegerTypedefed(
738 const TypedefDecl *TypedefDcl) {
739
740 QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
741 if (NSAPIObj->isObjCNSIntegerType(qt))
742 NSIntegerTypedefed = TypedefDcl;
743 else if (NSAPIObj->isObjCNSUIntegerType(qt))
744 NSUIntegerTypedefed = TypedefDcl;
745}
746
Fariborz Jahanian11dd4b12013-10-11 21:34:56 +0000747bool ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx,
Fariborz Jahanian92463272013-07-18 20:11:45 +0000748 const EnumDecl *EnumDcl,
749 const TypedefDecl *TypedefDcl) {
750 if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() ||
Fariborz Jahanian059e05e2013-10-15 00:00:28 +0000751 EnumDcl->isDeprecated())
752 return false;
753 if (!TypedefDcl) {
754 if (NSIntegerTypedefed) {
755 TypedefDcl = NSIntegerTypedefed;
756 NSIntegerTypedefed = 0;
757 }
758 else if (NSUIntegerTypedefed) {
759 TypedefDcl = NSUIntegerTypedefed;
760 NSUIntegerTypedefed = 0;
761 }
762 else
763 return false;
764 unsigned FileIdOfTypedefDcl =
765 PP.getSourceManager().getFileID(TypedefDcl->getLocation()).getHashValue();
766 unsigned FileIdOfEnumDcl =
767 PP.getSourceManager().getFileID(EnumDcl->getLocation()).getHashValue();
768 if (FileIdOfTypedefDcl != FileIdOfEnumDcl)
769 return false;
770 }
771 if (TypedefDcl->isDeprecated())
Fariborz Jahanian11dd4b12013-10-11 21:34:56 +0000772 return false;
Fariborz Jahanian92463272013-07-18 20:11:45 +0000773
774 QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000775 bool IsNSIntegerType = NSAPIObj->isObjCNSIntegerType(qt);
776 bool IsNSUIntegerType = !IsNSIntegerType && NSAPIObj->isObjCNSUIntegerType(qt);
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000777
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000778 if (!IsNSIntegerType && !IsNSUIntegerType) {
779 // Also check for typedef enum {...} TD;
780 if (const EnumType *EnumTy = qt->getAs<EnumType>()) {
781 if (EnumTy->getDecl() == EnumDcl) {
Fariborz Jahaniana23f4fb2013-08-30 00:10:37 +0000782 bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl);
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000783 if (NSOptions) {
784 if (!Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
Fariborz Jahanian11dd4b12013-10-11 21:34:56 +0000785 return false;
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000786 }
787 else if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
Fariborz Jahanian11dd4b12013-10-11 21:34:56 +0000788 return false;
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000789 edit::Commit commit(*Editor);
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000790 rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000791 Editor->commit(commit);
Fariborz Jahaniande79e812013-10-17 22:23:32 +0000792 return true;
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000793 }
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000794 }
Fariborz Jahanian11dd4b12013-10-11 21:34:56 +0000795 return false;
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000796 }
Fariborz Jahanian92463272013-07-18 20:11:45 +0000797
Fariborz Jahanianff3476e2013-08-30 17:46:01 +0000798 // We may still use NS_OPTIONS based on what we find in the enumertor list.
799 bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl);
Fariborz Jahanian92463272013-07-18 20:11:45 +0000800 // NS_ENUM must be available.
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000801 if (IsNSIntegerType && !Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
Fariborz Jahanian11dd4b12013-10-11 21:34:56 +0000802 return false;
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000803 // NS_OPTIONS must be available.
804 if (IsNSUIntegerType && !Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
Fariborz Jahanian11dd4b12013-10-11 21:34:56 +0000805 return false;
Fariborz Jahanian92463272013-07-18 20:11:45 +0000806 edit::Commit commit(*Editor);
Fariborz Jahanian059e05e2013-10-15 00:00:28 +0000807 bool Res = rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj,
808 commit, IsNSIntegerType, NSOptions);
Fariborz Jahanian92463272013-07-18 20:11:45 +0000809 Editor->commit(commit);
Fariborz Jahanian059e05e2013-10-15 00:00:28 +0000810 return Res;
Fariborz Jahanian92463272013-07-18 20:11:45 +0000811}
812
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000813static void ReplaceWithInstancetype(const ObjCMigrateASTConsumer &ASTC,
814 ObjCMethodDecl *OM) {
815 SourceRange R;
816 std::string ClassString;
817 if (TypeSourceInfo *TSInfo = OM->getResultTypeSourceInfo()) {
818 TypeLoc TL = TSInfo->getTypeLoc();
819 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc());
820 ClassString = "instancetype";
821 }
822 else {
823 R = SourceRange(OM->getLocStart(), OM->getLocStart());
824 ClassString = OM->isInstanceMethod() ? '-' : '+';
825 ClassString += " (instancetype)";
826 }
827 edit::Commit commit(*ASTC.Editor);
828 commit.replace(R, ClassString);
829 ASTC.Editor->commit(commit);
830}
831
Fariborz Jahanian7c87b432013-10-10 18:23:13 +0000832static void ReplaceWithClasstype(const ObjCMigrateASTConsumer &ASTC,
833 ObjCMethodDecl *OM) {
834 ObjCInterfaceDecl *IDecl = OM->getClassInterface();
835 SourceRange R;
836 std::string ClassString;
837 if (TypeSourceInfo *TSInfo = OM->getResultTypeSourceInfo()) {
838 TypeLoc TL = TSInfo->getTypeLoc();
839 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); {
840 ClassString = IDecl->getName();
841 ClassString += "*";
842 }
843 }
844 else {
845 R = SourceRange(OM->getLocStart(), OM->getLocStart());
846 ClassString = "+ (";
847 ClassString += IDecl->getName(); ClassString += "*)";
848 }
849 edit::Commit commit(*ASTC.Editor);
850 commit.replace(R, ClassString);
851 ASTC.Editor->commit(commit);
852}
853
Fariborz Jahanian670ef262013-07-23 23:34:42 +0000854void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx,
855 ObjCContainerDecl *CDecl,
856 ObjCMethodDecl *OM) {
Fariborz Jahanian71221352013-07-23 22:42:28 +0000857 ObjCInstanceTypeFamily OIT_Family =
858 Selector::getInstTypeMethodFamily(OM->getSelector());
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000859
Fariborz Jahanian267bae32013-07-24 19:18:37 +0000860 std::string ClassName;
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000861 switch (OIT_Family) {
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000862 case OIT_None:
863 migrateFactoryMethod(Ctx, CDecl, OM);
864 return;
Fariborz Jahanian7dd71432013-08-28 21:23:00 +0000865 case OIT_Array:
866 ClassName = "NSArray";
867 break;
868 case OIT_Dictionary:
869 ClassName = "NSDictionary";
870 break;
Fariborz Jahanian9275c682013-08-02 20:54:18 +0000871 case OIT_Singleton:
872 migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton);
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000873 return;
Fariborz Jahanian1c900bc2013-09-18 20:35:47 +0000874 case OIT_Init:
875 if (OM->getResultType()->isObjCIdType())
876 ReplaceWithInstancetype(*this, OM);
877 return;
Fariborz Jahanian7c87b432013-10-10 18:23:13 +0000878 case OIT_ReturnsSelf:
879 migrateFactoryMethod(Ctx, CDecl, OM, OIT_ReturnsSelf);
880 return;
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000881 }
Fariborz Jahanian71221352013-07-23 22:42:28 +0000882 if (!OM->getResultType()->isObjCIdType())
883 return;
884
885 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
886 if (!IDecl) {
887 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
888 IDecl = CatDecl->getClassInterface();
889 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
890 IDecl = ImpDecl->getClassInterface();
891 }
Fariborz Jahanian267bae32013-07-24 19:18:37 +0000892 if (!IDecl ||
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000893 !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) {
894 migrateFactoryMethod(Ctx, CDecl, OM);
Fariborz Jahanian71221352013-07-23 22:42:28 +0000895 return;
Fariborz Jahanian280954a2013-07-24 18:31:42 +0000896 }
Fariborz Jahanianc4291852013-08-02 18:00:53 +0000897 ReplaceWithInstancetype(*this, OM);
Fariborz Jahanian71221352013-07-23 22:42:28 +0000898}
899
Fariborz Jahaniand0fbf6c2013-08-30 23:52:08 +0000900static bool TypeIsInnerPointer(QualType T) {
901 if (!T->isAnyPointerType())
902 return false;
903 if (T->isObjCObjectPointerType() || T->isObjCBuiltinType() ||
Fariborz Jahanian73466ca2013-09-26 21:43:47 +0000904 T->isBlockPointerType() || T->isFunctionPointerType() ||
905 ento::coreFoundation::isCFObjectRef(T))
Fariborz Jahaniand0fbf6c2013-08-30 23:52:08 +0000906 return false;
Fariborz Jahanian9d5fffb2013-09-09 23:56:14 +0000907 // Also, typedef-of-pointer-to-incomplete-struct is something that we assume
908 // is not an innter pointer type.
909 QualType OrigT = T;
910 while (const TypedefType *TD = dyn_cast<TypedefType>(T.getTypePtr()))
911 T = TD->getDecl()->getUnderlyingType();
912 if (OrigT == T || !T->isPointerType())
913 return true;
914 const PointerType* PT = T->getAs<PointerType>();
915 QualType UPointeeT = PT->getPointeeType().getUnqualifiedType();
916 if (UPointeeT->isRecordType()) {
917 const RecordType *RecordTy = UPointeeT->getAs<RecordType>();
918 if (!RecordTy->getDecl()->isCompleteDefinition())
919 return false;
920 }
Fariborz Jahaniand0fbf6c2013-08-30 23:52:08 +0000921 return true;
922}
923
Fariborz Jahanian071b98e2013-11-01 00:26:48 +0000924/// \brief Check whether the two versions match.
925static bool versionsMatch(const VersionTuple &X, const VersionTuple &Y) {
926 return (X == Y);
927}
928
929/// AvailabilityAttrsMatch - This routine checks that if comparing two
930/// availability attributes, all their components match. It returns
931/// true, if not dealing with availability or when all components of
932/// availability attributes match. This routine is only called when
933/// the attributes are of the same kind.
934static bool AvailabilityAttrsMatch(Attr *At1, Attr *At2) {
935 const AvailabilityAttr *AA1 = dyn_cast<AvailabilityAttr>(At1);
936 if (!AA1)
Fariborz Jahanian2ba62a72013-09-18 17:22:25 +0000937 return true;
Fariborz Jahanian071b98e2013-11-01 00:26:48 +0000938 const AvailabilityAttr *AA2 = dyn_cast<AvailabilityAttr>(At2);
Fariborz Jahanian2ba62a72013-09-18 17:22:25 +0000939
Fariborz Jahanian071b98e2013-11-01 00:26:48 +0000940 VersionTuple Introduced1 = AA1->getIntroduced();
941 VersionTuple Deprecated1 = AA1->getDeprecated();
942 VersionTuple Obsoleted1 = AA1->getObsoleted();
943 bool IsUnavailable1 = AA1->getUnavailable();
944 VersionTuple Introduced2 = AA2->getIntroduced();
945 VersionTuple Deprecated2 = AA2->getDeprecated();
946 VersionTuple Obsoleted2 = AA2->getObsoleted();
947 bool IsUnavailable2 = AA2->getUnavailable();
948 return (versionsMatch(Introduced1, Introduced2) &&
949 versionsMatch(Deprecated1, Deprecated2) &&
950 versionsMatch(Obsoleted1, Obsoleted2) &&
951 IsUnavailable1 == IsUnavailable2);
952
953}
954
955static bool MatchTwoAttributeLists(const AttrVec &Attrs1, const AttrVec &Attrs2,
956 bool &AvailabilityArgsMatch) {
Fariborz Jahanian2ba62a72013-09-18 17:22:25 +0000957 // This list is very small, so this need not be optimized.
958 for (unsigned i = 0, e = Attrs1.size(); i != e; i++) {
959 bool match = false;
960 for (unsigned j = 0, f = Attrs2.size(); j != f; j++) {
Fariborz Jahanian071b98e2013-11-01 00:26:48 +0000961 // Matching attribute kind only. Except for Availabilty attributes,
962 // we are not getting into details of the attributes. For all practical purposes
Fariborz Jahanian2ba62a72013-09-18 17:22:25 +0000963 // this is sufficient.
964 if (Attrs1[i]->getKind() == Attrs2[j]->getKind()) {
Fariborz Jahanian071b98e2013-11-01 00:26:48 +0000965 if (AvailabilityArgsMatch)
966 AvailabilityArgsMatch = AvailabilityAttrsMatch(Attrs1[i], Attrs2[j]);
Fariborz Jahanian2ba62a72013-09-18 17:22:25 +0000967 match = true;
968 break;
969 }
970 }
971 if (!match)
972 return false;
973 }
974 return true;
975}
976
Fariborz Jahanian071b98e2013-11-01 00:26:48 +0000977/// AttributesMatch - This routine checks list of attributes for two
978/// decls. It returns false, if there is a mismatch in kind of
979/// attributes seen in the decls. It returns true if the two decls
980/// have list of same kind of attributes. Furthermore, when there
981/// are availability attributes in the two decls, it sets the
982/// AvailabilityArgsMatch to false if availability attributes have
983/// different versions, etc.
984static bool AttributesMatch(const Decl *Decl1, const Decl *Decl2,
985 bool &AvailabilityArgsMatch) {
986 if (!Decl1->hasAttrs() || !Decl2->hasAttrs()) {
987 AvailabilityArgsMatch = (Decl1->hasAttrs() == Decl2->hasAttrs());
988 return true;
989 }
990 AvailabilityArgsMatch = true;
991 const AttrVec &Attrs1 = Decl1->getAttrs();
992 const AttrVec &Attrs2 = Decl2->getAttrs();
993 bool match = MatchTwoAttributeLists(Attrs1, Attrs2, AvailabilityArgsMatch);
994 if (match && (Attrs2.size() > Attrs1.size()))
995 return MatchTwoAttributeLists(Attrs2, Attrs1, AvailabilityArgsMatch);
996 return match;
997}
998
Fariborz Jahanian5d783df2013-09-27 22:55:54 +0000999static bool IsValidIdentifier(ASTContext &Ctx,
1000 const char *Name) {
1001 if (!isIdentifierHead(Name[0]))
1002 return false;
1003 std::string NameString = Name;
1004 NameString[0] = toLowercase(NameString[0]);
1005 IdentifierInfo *II = &Ctx.Idents.get(NameString);
1006 return II->getTokenID() == tok::identifier;
1007}
1008
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001009bool ObjCMigrateASTConsumer::migrateProperty(ASTContext &Ctx,
Fariborz Jahanian92f72422013-09-17 19:00:30 +00001010 ObjCContainerDecl *D,
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001011 ObjCMethodDecl *Method) {
1012 if (Method->isPropertyAccessor() || !Method->isInstanceMethod() ||
1013 Method->param_size() != 0)
1014 return false;
1015 // Is this method candidate to be a getter?
1016 QualType GRT = Method->getResultType();
1017 if (GRT->isVoidType())
1018 return false;
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001019
1020 Selector GetterSelector = Method->getSelector();
Fariborz Jahanian7391a7b5a2013-09-25 00:17:07 +00001021 ObjCInstanceTypeFamily OIT_Family =
1022 Selector::getInstTypeMethodFamily(GetterSelector);
1023
1024 if (OIT_Family != OIT_None)
1025 return false;
1026
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001027 IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);
1028 Selector SetterSelector =
1029 SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
1030 PP.getSelectorTable(),
1031 getterName);
Fariborz Jahanian92f72422013-09-17 19:00:30 +00001032 ObjCMethodDecl *SetterMethod = D->getInstanceMethod(SetterSelector);
Fariborz Jahanianca399602013-09-11 17:05:15 +00001033 unsigned LengthOfPrefix = 0;
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001034 if (!SetterMethod) {
1035 // try a different naming convention for getter: isXxxxx
1036 StringRef getterNameString = getterName->getName();
Fariborz Jahanianca399602013-09-11 17:05:15 +00001037 bool IsPrefix = getterNameString.startswith("is");
Fariborz Jahanian4c3d9c52013-09-17 19:38:55 +00001038 // Note that we don't want to change an isXXX method of retainable object
1039 // type to property (readonly or otherwise).
1040 if (IsPrefix && GRT->isObjCRetainableType())
1041 return false;
1042 if (IsPrefix || getterNameString.startswith("get")) {
Fariborz Jahanianca399602013-09-11 17:05:15 +00001043 LengthOfPrefix = (IsPrefix ? 2 : 3);
1044 const char *CGetterName = getterNameString.data() + LengthOfPrefix;
1045 // Make sure that first character after "is" or "get" prefix can
1046 // start an identifier.
Fariborz Jahanian5d783df2013-09-27 22:55:54 +00001047 if (!IsValidIdentifier(Ctx, CGetterName))
Fariborz Jahanianca399602013-09-11 17:05:15 +00001048 return false;
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001049 if (CGetterName[0] && isUppercase(CGetterName[0])) {
1050 getterName = &Ctx.Idents.get(CGetterName);
1051 SetterSelector =
1052 SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
1053 PP.getSelectorTable(),
1054 getterName);
Fariborz Jahanian92f72422013-09-17 19:00:30 +00001055 SetterMethod = D->getInstanceMethod(SetterSelector);
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001056 }
1057 }
1058 }
Fariborz Jahanian4c3d9c52013-09-17 19:38:55 +00001059
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001060 if (SetterMethod) {
Fariborz Jahanianc1213862013-10-02 21:32:39 +00001061 if ((ASTMigrateActions & FrontendOptions::ObjCMT_ReadwriteProperty) == 0)
1062 return false;
Fariborz Jahanian071b98e2013-11-01 00:26:48 +00001063 bool AvailabilityArgsMatch;
Fariborz Jahanian2ba62a72013-09-18 17:22:25 +00001064 if (SetterMethod->isDeprecated() ||
Fariborz Jahanian071b98e2013-11-01 00:26:48 +00001065 !AttributesMatch(Method, SetterMethod, AvailabilityArgsMatch))
Fariborz Jahanianf6c65052013-09-17 22:41:25 +00001066 return false;
Fariborz Jahanian2ba62a72013-09-18 17:22:25 +00001067
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001068 // Is this a valid setter, matching the target getter?
1069 QualType SRT = SetterMethod->getResultType();
1070 if (!SRT->isVoidType())
1071 return false;
1072 const ParmVarDecl *argDecl = *SetterMethod->param_begin();
1073 QualType ArgType = argDecl->getType();
Fariborz Jahanian2ba62a72013-09-18 17:22:25 +00001074 if (!Ctx.hasSameUnqualifiedType(ArgType, GRT))
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001075 return false;
1076 edit::Commit commit(*Editor);
1077 rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit,
Fariborz Jahanian20a11242013-10-09 19:06:08 +00001078 LengthOfPrefix,
1079 (ASTMigrateActions &
Fariborz Jahanian071b98e2013-11-01 00:26:48 +00001080 FrontendOptions::ObjCMT_AtomicProperty) != 0,
1081 AvailabilityArgsMatch);
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001082 Editor->commit(commit);
1083 return true;
1084 }
Fariborz Jahanian182486c2013-10-02 17:08:12 +00001085 else if (ASTMigrateActions & FrontendOptions::ObjCMT_ReadonlyProperty) {
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001086 // Try a non-void method with no argument (and no setter or property of same name
1087 // as a 'readonly' property.
1088 edit::Commit commit(*Editor);
1089 rewriteToObjCProperty(Method, 0 /*SetterMethod*/, *NSAPIObj, commit,
Fariborz Jahanian20a11242013-10-09 19:06:08 +00001090 LengthOfPrefix,
1091 (ASTMigrateActions &
Fariborz Jahanian071b98e2013-11-01 00:26:48 +00001092 FrontendOptions::ObjCMT_AtomicProperty) != 0,
1093 /*AvailabilityArgsMatch*/false);
Fariborz Jahanian215f96c2013-09-06 23:45:20 +00001094 Editor->commit(commit);
1095 return true;
1096 }
1097 return false;
1098}
1099
Fariborz Jahaniand0fbf6c2013-08-30 23:52:08 +00001100void ObjCMigrateASTConsumer::migrateNsReturnsInnerPointer(ASTContext &Ctx,
1101 ObjCMethodDecl *OM) {
Fariborz Jahanian10b74352013-09-24 20:20:52 +00001102 if (OM->isImplicit() ||
Fariborz Jahanian7c1a4452013-09-26 22:43:41 +00001103 !OM->isInstanceMethod() ||
Fariborz Jahanian10b74352013-09-24 20:20:52 +00001104 OM->hasAttr<ObjCReturnsInnerPointerAttr>())
Fariborz Jahaniand0fbf6c2013-08-30 23:52:08 +00001105 return;
1106
1107 QualType RT = OM->getResultType();
1108 if (!TypeIsInnerPointer(RT) ||
1109 !Ctx.Idents.get("NS_RETURNS_INNER_POINTER").hasMacroDefinition())
1110 return;
1111
1112 edit::Commit commit(*Editor);
1113 commit.insertBefore(OM->getLocEnd(), " NS_RETURNS_INNER_POINTER");
1114 Editor->commit(commit);
1115}
1116
Fariborz Jahanian10b74352013-09-24 20:20:52 +00001117void ObjCMigrateASTConsumer::migratePropertyNsReturnsInnerPointer(ASTContext &Ctx,
1118 ObjCPropertyDecl *P) {
1119 QualType T = P->getType();
1120
1121 if (!TypeIsInnerPointer(T) ||
1122 !Ctx.Idents.get("NS_RETURNS_INNER_POINTER").hasMacroDefinition())
1123 return;
1124 edit::Commit commit(*Editor);
1125 commit.insertBefore(P->getLocEnd(), " NS_RETURNS_INNER_POINTER ");
1126 Editor->commit(commit);
1127}
1128
Fariborz Jahanian8c45e282013-10-02 22:49:59 +00001129void ObjCMigrateASTConsumer::migrateAllMethodInstaceType(ASTContext &Ctx,
Fariborz Jahanian71221352013-07-23 22:42:28 +00001130 ObjCContainerDecl *CDecl) {
Fariborz Jahaniane47a14a2013-09-18 15:43:52 +00001131 if (CDecl->isDeprecated())
1132 return;
1133
Fariborz Jahanian71221352013-07-23 22:42:28 +00001134 // migrate methods which can have instancetype as their result type.
1135 for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(),
1136 MEnd = CDecl->meth_end();
1137 M != MEnd; ++M) {
1138 ObjCMethodDecl *Method = (*M);
Fariborz Jahanian75226d52013-09-17 21:56:04 +00001139 if (Method->isDeprecated())
1140 continue;
Fariborz Jahanian71221352013-07-23 22:42:28 +00001141 migrateMethodInstanceType(Ctx, CDecl, Method);
1142 }
1143}
1144
Fariborz Jahanianc4291852013-08-02 18:00:53 +00001145void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx,
1146 ObjCContainerDecl *CDecl,
Fariborz Jahanian9275c682013-08-02 20:54:18 +00001147 ObjCMethodDecl *OM,
1148 ObjCInstanceTypeFamily OIT_Family) {
Fariborz Jahanian3bfc35e2013-08-02 22:34:18 +00001149 if (OM->isInstanceMethod() ||
1150 OM->getResultType() == Ctx.getObjCInstanceType() ||
1151 !OM->getResultType()->isObjCIdType())
Fariborz Jahanianc4291852013-08-02 18:00:53 +00001152 return;
1153
1154 // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class
1155 // NSYYYNamE with matching names be at least 3 characters long.
1156 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
1157 if (!IDecl) {
1158 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
1159 IDecl = CatDecl->getClassInterface();
1160 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
1161 IDecl = ImpDecl->getClassInterface();
1162 }
1163 if (!IDecl)
1164 return;
1165
1166 std::string StringClassName = IDecl->getName();
1167 StringRef LoweredClassName(StringClassName);
1168 std::string StringLoweredClassName = LoweredClassName.lower();
1169 LoweredClassName = StringLoweredClassName;
1170
1171 IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0);
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +00001172 // Handle method with no name at its first selector slot; e.g. + (id):(int)x.
1173 if (!MethodIdName)
1174 return;
1175
Fariborz Jahanianc4291852013-08-02 18:00:53 +00001176 std::string MethodName = MethodIdName->getName();
Fariborz Jahanian7c87b432013-10-10 18:23:13 +00001177 if (OIT_Family == OIT_Singleton || OIT_Family == OIT_ReturnsSelf) {
Fariborz Jahanian9275c682013-08-02 20:54:18 +00001178 StringRef STRefMethodName(MethodName);
1179 size_t len = 0;
1180 if (STRefMethodName.startswith("standard"))
1181 len = strlen("standard");
1182 else if (STRefMethodName.startswith("shared"))
1183 len = strlen("shared");
1184 else if (STRefMethodName.startswith("default"))
1185 len = strlen("default");
1186 else
1187 return;
1188 MethodName = STRefMethodName.substr(len);
1189 }
Fariborz Jahanianc4291852013-08-02 18:00:53 +00001190 std::string MethodNameSubStr = MethodName.substr(0, 3);
1191 StringRef MethodNamePrefix(MethodNameSubStr);
1192 std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower();
1193 MethodNamePrefix = StringLoweredMethodNamePrefix;
1194 size_t Ix = LoweredClassName.rfind(MethodNamePrefix);
1195 if (Ix == StringRef::npos)
1196 return;
1197 std::string ClassNamePostfix = LoweredClassName.substr(Ix);
1198 StringRef LoweredMethodName(MethodName);
1199 std::string StringLoweredMethodName = LoweredMethodName.lower();
1200 LoweredMethodName = StringLoweredMethodName;
1201 if (!LoweredMethodName.startswith(ClassNamePostfix))
1202 return;
Fariborz Jahanian7c87b432013-10-10 18:23:13 +00001203 if (OIT_Family == OIT_ReturnsSelf)
1204 ReplaceWithClasstype(*this, OM);
1205 else
1206 ReplaceWithInstancetype(*this, OM);
Fariborz Jahanianc4291852013-08-02 18:00:53 +00001207}
1208
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +00001209static bool IsVoidStarType(QualType Ty) {
1210 if (!Ty->isPointerType())
1211 return false;
1212
1213 while (const TypedefType *TD = dyn_cast<TypedefType>(Ty.getTypePtr()))
1214 Ty = TD->getDecl()->getUnderlyingType();
1215
1216 // Is the type void*?
1217 const PointerType* PT = Ty->getAs<PointerType>();
1218 if (PT->getPointeeType().getUnqualifiedType()->isVoidType())
1219 return true;
1220 return IsVoidStarType(PT->getPointeeType());
1221}
1222
Fariborz Jahanian94279392013-08-20 18:54:39 +00001223/// AuditedType - This routine audits the type AT and returns false if it is one of known
1224/// CF object types or of the "void *" variety. It returns true if we don't care about the type
1225/// such as a non-pointer or pointers which have no ownership issues (such as "int *").
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001226static bool AuditedType (QualType AT) {
1227 if (!AT->isAnyPointerType() && !AT->isBlockPointerType())
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +00001228 return true;
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +00001229 // FIXME. There isn't much we can say about CF pointer type; or is there?
Fariborz Jahanian94279392013-08-20 18:54:39 +00001230 if (ento::coreFoundation::isCFObjectRef(AT) ||
1231 IsVoidStarType(AT) ||
1232 // If an ObjC object is type, assuming that it is not a CF function and
1233 // that it is an un-audited function.
Fariborz Jahanian2e9c19c2013-08-22 22:27:36 +00001234 AT->isObjCObjectPointerType() || AT->isObjCBuiltinType())
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +00001235 return false;
Fariborz Jahanian94279392013-08-20 18:54:39 +00001236 // All other pointers are assumed audited as harmless.
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +00001237 return true;
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +00001238}
1239
Fariborz Jahanian301b5212013-08-20 22:42:13 +00001240void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) {
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001241 if (CFFunctionIBCandidates.empty())
1242 return;
Fariborz Jahanian301b5212013-08-20 22:42:13 +00001243 if (!Ctx.Idents.get("CF_IMPLICIT_BRIDGING_ENABLED").hasMacroDefinition()) {
1244 CFFunctionIBCandidates.clear();
Fariborz Jahanianb8343522013-08-20 23:35:26 +00001245 FileId = 0;
Fariborz Jahanian301b5212013-08-20 22:42:13 +00001246 return;
1247 }
1248 // Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001249 const Decl *FirstFD = CFFunctionIBCandidates[0];
1250 const Decl *LastFD =
1251 CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1];
Fariborz Jahanian301b5212013-08-20 22:42:13 +00001252 const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n";
1253 edit::Commit commit(*Editor);
1254 commit.insertBefore(FirstFD->getLocStart(), PragmaString);
1255 PragmaString = "\n\nCF_IMPLICIT_BRIDGING_DISABLED\n";
1256 SourceLocation EndLoc = LastFD->getLocEnd();
1257 // get location just past end of function location.
1258 EndLoc = PP.getLocForEndOfToken(EndLoc);
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001259 if (isa<FunctionDecl>(LastFD)) {
1260 // For Methods, EndLoc points to the ending semcolon. So,
1261 // not of these extra work is needed.
1262 Token Tok;
1263 // get locaiton of token that comes after end of function.
1264 bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true);
1265 if (!Failed)
1266 EndLoc = Tok.getLocation();
1267 }
Fariborz Jahanian301b5212013-08-20 22:42:13 +00001268 commit.insertAfterToken(EndLoc, PragmaString);
1269 Editor->commit(commit);
Fariborz Jahanianb8343522013-08-20 23:35:26 +00001270 FileId = 0;
Fariborz Jahanian301b5212013-08-20 22:42:13 +00001271 CFFunctionIBCandidates.clear();
1272}
1273
Fariborz Jahanian4f64dd22013-08-22 21:40:15 +00001274void ObjCMigrateASTConsumer::migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl) {
Fariborz Jahanian75226d52013-09-17 21:56:04 +00001275 if (Decl->isDeprecated())
1276 return;
1277
Fariborz Jahanian4f64dd22013-08-22 21:40:15 +00001278 if (Decl->hasAttr<CFAuditedTransferAttr>()) {
Fariborz Jahanianc6dfd3f2013-08-19 22:00:50 +00001279 assert(CFFunctionIBCandidates.empty() &&
Fariborz Jahanian4f64dd22013-08-22 21:40:15 +00001280 "Cannot have audited functions/methods inside user "
Fariborz Jahanianc6dfd3f2013-08-19 22:00:50 +00001281 "provided CF_IMPLICIT_BRIDGING_ENABLE");
1282 return;
1283 }
1284
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +00001285 // Finction must be annotated first.
Fariborz Jahanian89f6d102013-09-04 00:10:06 +00001286 if (const FunctionDecl *FuncDecl = dyn_cast<FunctionDecl>(Decl)) {
1287 CF_BRIDGING_KIND AuditKind = migrateAddFunctionAnnotation(Ctx, FuncDecl);
1288 if (AuditKind == CF_BRIDGING_ENABLE) {
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001289 CFFunctionIBCandidates.push_back(Decl);
1290 if (!FileId)
1291 FileId = PP.getSourceManager().getFileID(Decl->getLocation()).getHashValue();
1292 }
Fariborz Jahanian89f6d102013-09-04 00:10:06 +00001293 else if (AuditKind == CF_BRIDGING_MAY_INCLUDE) {
1294 if (!CFFunctionIBCandidates.empty()) {
1295 CFFunctionIBCandidates.push_back(Decl);
1296 if (!FileId)
1297 FileId = PP.getSourceManager().getFileID(Decl->getLocation()).getHashValue();
1298 }
1299 }
1300 else
1301 AnnotateImplicitBridging(Ctx);
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001302 }
Fariborz Jahanian89f6d102013-09-04 00:10:06 +00001303 else {
1304 migrateAddMethodAnnotation(Ctx, cast<ObjCMethodDecl>(Decl));
Fariborz Jahanian301b5212013-08-20 22:42:13 +00001305 AnnotateImplicitBridging(Ctx);
Fariborz Jahanian89f6d102013-09-04 00:10:06 +00001306 }
Fariborz Jahanian2ec4d7b2013-08-16 23:35:05 +00001307}
1308
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001309void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx,
1310 const CallEffects &CE,
Fariborz Jahanian7ca1d302013-08-27 23:56:54 +00001311 const FunctionDecl *FuncDecl,
1312 bool ResultAnnotated) {
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001313 // Annotate function.
Fariborz Jahanian7ca1d302013-08-27 23:56:54 +00001314 if (!ResultAnnotated) {
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +00001315 RetEffect Ret = CE.getReturnValue();
1316 const char *AnnotationString = 0;
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001317 if (Ret.getObjKind() == RetEffect::CF) {
1318 if (Ret.isOwned() &&
1319 Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition())
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +00001320 AnnotationString = " CF_RETURNS_RETAINED";
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001321 else if (Ret.notOwned() &&
1322 Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition())
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +00001323 AnnotationString = " CF_RETURNS_NOT_RETAINED";
1324 }
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001325 else if (Ret.getObjKind() == RetEffect::ObjC) {
1326 if (Ret.isOwned() &&
1327 Ctx.Idents.get("NS_RETURNS_RETAINED").hasMacroDefinition())
1328 AnnotationString = " NS_RETURNS_RETAINED";
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001329 }
1330
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +00001331 if (AnnotationString) {
1332 edit::Commit commit(*Editor);
1333 commit.insertAfterToken(FuncDecl->getLocEnd(), AnnotationString);
1334 Editor->commit(commit);
1335 }
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +00001336 }
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +00001337 llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs();
1338 unsigned i = 0;
1339 for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),
1340 pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {
Fariborz Jahanianb918d7a2013-08-21 19:37:47 +00001341 const ParmVarDecl *pd = *pi;
Fariborz Jahanian9ef4a262013-08-19 19:13:34 +00001342 ArgEffect AE = AEArgs[i];
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001343 if (AE == DecRef && !pd->getAttr<CFConsumedAttr>() &&
1344 Ctx.Idents.get("CF_CONSUMED").hasMacroDefinition()) {
1345 edit::Commit commit(*Editor);
1346 commit.insertBefore(pd->getLocation(), "CF_CONSUMED ");
1347 Editor->commit(commit);
Fariborz Jahanian94279392013-08-20 18:54:39 +00001348 }
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001349 else if (AE == DecRefMsg && !pd->getAttr<NSConsumedAttr>() &&
1350 Ctx.Idents.get("NS_CONSUMED").hasMacroDefinition()) {
1351 edit::Commit commit(*Editor);
1352 commit.insertBefore(pd->getLocation(), "NS_CONSUMED ");
1353 Editor->commit(commit);
1354 }
Fariborz Jahanian84ac1de2013-08-15 21:44:38 +00001355 }
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001356}
1357
1358
1359ObjCMigrateASTConsumer::CF_BRIDGING_KIND
1360 ObjCMigrateASTConsumer::migrateAddFunctionAnnotation(
1361 ASTContext &Ctx,
1362 const FunctionDecl *FuncDecl) {
1363 if (FuncDecl->hasBody())
1364 return CF_BRIDGING_NONE;
1365
1366 CallEffects CE = CallEffects::getEffect(FuncDecl);
1367 bool FuncIsReturnAnnotated = (FuncDecl->getAttr<CFReturnsRetainedAttr>() ||
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001368 FuncDecl->getAttr<CFReturnsNotRetainedAttr>() ||
1369 FuncDecl->getAttr<NSReturnsRetainedAttr>() ||
Fariborz Jahanianc24879e2013-09-05 23:04:33 +00001370 FuncDecl->getAttr<NSReturnsNotRetainedAttr>() ||
1371 FuncDecl->getAttr<NSReturnsAutoreleasedAttr>());
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001372
1373 // Trivial case of when funciton is annotated and has no argument.
1374 if (FuncIsReturnAnnotated && FuncDecl->getNumParams() == 0)
1375 return CF_BRIDGING_NONE;
1376
1377 bool ReturnCFAudited = false;
1378 if (!FuncIsReturnAnnotated) {
1379 RetEffect Ret = CE.getReturnValue();
1380 if (Ret.getObjKind() == RetEffect::CF &&
Fariborz Jahanian89f6d102013-09-04 00:10:06 +00001381 (Ret.isOwned() || Ret.notOwned()))
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001382 ReturnCFAudited = true;
1383 else if (!AuditedType(FuncDecl->getResultType()))
1384 return CF_BRIDGING_NONE;
1385 }
1386
1387 // At this point result type is audited for potential inclusion.
1388 // Now, how about argument types.
1389 llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs();
1390 unsigned i = 0;
1391 bool ArgCFAudited = false;
1392 for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),
1393 pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {
1394 const ParmVarDecl *pd = *pi;
1395 ArgEffect AE = AEArgs[i];
1396 if (AE == DecRef /*CFConsumed annotated*/ || AE == IncRef) {
1397 if (AE == DecRef && !pd->getAttr<CFConsumedAttr>())
1398 ArgCFAudited = true;
1399 else if (AE == IncRef)
1400 ArgCFAudited = true;
1401 }
1402 else {
1403 QualType AT = pd->getType();
1404 if (!AuditedType(AT)) {
Fariborz Jahanian7ca1d302013-08-27 23:56:54 +00001405 AddCFAnnotations(Ctx, CE, FuncDecl, FuncIsReturnAnnotated);
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001406 return CF_BRIDGING_NONE;
1407 }
1408 }
1409 }
1410 if (ReturnCFAudited || ArgCFAudited)
1411 return CF_BRIDGING_ENABLE;
1412
1413 return CF_BRIDGING_MAY_INCLUDE;
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +00001414}
1415
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001416void ObjCMigrateASTConsumer::migrateARCSafeAnnotation(ASTContext &Ctx,
1417 ObjCContainerDecl *CDecl) {
Fariborz Jahanian75226d52013-09-17 21:56:04 +00001418 if (!isa<ObjCInterfaceDecl>(CDecl) || CDecl->isDeprecated())
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001419 return;
1420
1421 // migrate methods which can have instancetype as their result type.
1422 for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(),
1423 MEnd = CDecl->meth_end();
1424 M != MEnd; ++M) {
1425 ObjCMethodDecl *Method = (*M);
Fariborz Jahanian4f64dd22013-08-22 21:40:15 +00001426 migrateCFAnnotation(Ctx, Method);
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001427 }
1428}
1429
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001430void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx,
1431 const CallEffects &CE,
Fariborz Jahanian7ca1d302013-08-27 23:56:54 +00001432 const ObjCMethodDecl *MethodDecl,
1433 bool ResultAnnotated) {
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001434 // Annotate function.
Fariborz Jahanian7ca1d302013-08-27 23:56:54 +00001435 if (!ResultAnnotated) {
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001436 RetEffect Ret = CE.getReturnValue();
1437 const char *AnnotationString = 0;
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001438 if (Ret.getObjKind() == RetEffect::CF) {
1439 if (Ret.isOwned() &&
1440 Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition())
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001441 AnnotationString = " CF_RETURNS_RETAINED";
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001442 else if (Ret.notOwned() &&
1443 Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition())
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001444 AnnotationString = " CF_RETURNS_NOT_RETAINED";
1445 }
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001446 else if (Ret.getObjKind() == RetEffect::ObjC) {
Fariborz Jahanianc24879e2013-09-05 23:04:33 +00001447 ObjCMethodFamily OMF = MethodDecl->getMethodFamily();
1448 switch (OMF) {
1449 case clang::OMF_alloc:
1450 case clang::OMF_new:
1451 case clang::OMF_copy:
1452 case clang::OMF_init:
1453 case clang::OMF_mutableCopy:
1454 break;
1455
1456 default:
1457 if (Ret.isOwned() &&
1458 Ctx.Idents.get("NS_RETURNS_RETAINED").hasMacroDefinition())
1459 AnnotationString = " NS_RETURNS_RETAINED";
Fariborz Jahanianc24879e2013-09-05 23:04:33 +00001460 break;
1461 }
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001462 }
1463
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001464 if (AnnotationString) {
1465 edit::Commit commit(*Editor);
1466 commit.insertBefore(MethodDecl->getLocEnd(), AnnotationString);
1467 Editor->commit(commit);
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001468 }
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001469 }
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001470 llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs();
1471 unsigned i = 0;
1472 for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(),
1473 pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) {
1474 const ParmVarDecl *pd = *pi;
1475 ArgEffect AE = AEArgs[i];
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001476 if (AE == DecRef && !pd->getAttr<CFConsumedAttr>() &&
1477 Ctx.Idents.get("CF_CONSUMED").hasMacroDefinition()) {
1478 edit::Commit commit(*Editor);
1479 commit.insertBefore(pd->getLocation(), "CF_CONSUMED ");
1480 Editor->commit(commit);
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001481 }
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001482 }
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001483}
1484
Fariborz Jahanian89f6d102013-09-04 00:10:06 +00001485void ObjCMigrateASTConsumer::migrateAddMethodAnnotation(
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001486 ASTContext &Ctx,
1487 const ObjCMethodDecl *MethodDecl) {
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001488 if (MethodDecl->hasBody() || MethodDecl->isImplicit())
Fariborz Jahanian89f6d102013-09-04 00:10:06 +00001489 return;
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001490
1491 CallEffects CE = CallEffects::getEffect(MethodDecl);
1492 bool MethodIsReturnAnnotated = (MethodDecl->getAttr<CFReturnsRetainedAttr>() ||
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001493 MethodDecl->getAttr<CFReturnsNotRetainedAttr>() ||
1494 MethodDecl->getAttr<NSReturnsRetainedAttr>() ||
Fariborz Jahanianc24879e2013-09-05 23:04:33 +00001495 MethodDecl->getAttr<NSReturnsNotRetainedAttr>() ||
1496 MethodDecl->getAttr<NSReturnsAutoreleasedAttr>());
1497
1498 if (CE.getReceiver() == DecRefMsg &&
1499 !MethodDecl->getAttr<NSConsumesSelfAttr>() &&
1500 MethodDecl->getMethodFamily() != OMF_init &&
1501 MethodDecl->getMethodFamily() != OMF_release &&
1502 Ctx.Idents.get("NS_CONSUMES_SELF").hasMacroDefinition()) {
1503 edit::Commit commit(*Editor);
1504 commit.insertBefore(MethodDecl->getLocEnd(), " NS_CONSUMES_SELF");
1505 Editor->commit(commit);
1506 }
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001507
1508 // Trivial case of when funciton is annotated and has no argument.
1509 if (MethodIsReturnAnnotated &&
1510 (MethodDecl->param_begin() == MethodDecl->param_end()))
Fariborz Jahanian89f6d102013-09-04 00:10:06 +00001511 return;
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001512
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001513 if (!MethodIsReturnAnnotated) {
1514 RetEffect Ret = CE.getReturnValue();
Fariborz Jahanian1a269272013-09-04 22:49:19 +00001515 if ((Ret.getObjKind() == RetEffect::CF ||
1516 Ret.getObjKind() == RetEffect::ObjC) &&
1517 (Ret.isOwned() || Ret.notOwned())) {
Fariborz Jahanian8b102302013-09-04 16:43:57 +00001518 AddCFAnnotations(Ctx, CE, MethodDecl, false);
1519 return;
1520 }
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001521 else if (!AuditedType(MethodDecl->getResultType()))
Fariborz Jahanian89f6d102013-09-04 00:10:06 +00001522 return;
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001523 }
1524
1525 // At this point result type is either annotated or audited.
1526 // Now, how about argument types.
1527 llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs();
1528 unsigned i = 0;
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001529 for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(),
1530 pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) {
1531 const ParmVarDecl *pd = *pi;
1532 ArgEffect AE = AEArgs[i];
Fariborz Jahanian8b102302013-09-04 16:43:57 +00001533 if ((AE == DecRef && !pd->getAttr<CFConsumedAttr>()) || AE == IncRef ||
1534 !AuditedType(pd->getType())) {
1535 AddCFAnnotations(Ctx, CE, MethodDecl, MethodIsReturnAnnotated);
1536 return;
Fariborz Jahanian63ffce22013-08-27 22:42:30 +00001537 }
1538 }
Fariborz Jahanian89f6d102013-09-04 00:10:06 +00001539 return;
Fariborz Jahaniandfe6ed92013-08-12 23:17:13 +00001540}
1541
Ted Kremenekf7639e12012-03-06 20:06:33 +00001542namespace {
1543
1544class RewritesReceiver : public edit::EditsReceiver {
1545 Rewriter &Rewrite;
1546
1547public:
1548 RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
1549
1550 virtual void insert(SourceLocation loc, StringRef text) {
1551 Rewrite.InsertText(loc, text);
1552 }
1553 virtual void replace(CharSourceRange range, StringRef text) {
1554 Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
1555 }
1556};
1557
1558}
1559
Fariborz Jahanian8f5225b2013-10-01 21:16:29 +00001560static bool
1561IsReallyASystemHeader(ASTContext &Ctx, const FileEntry *file, FileID FID) {
1562 bool Invalid = false;
1563 const SrcMgr::SLocEntry &SEntry =
1564 Ctx.getSourceManager().getSLocEntry(FID, &Invalid);
1565 if (!Invalid && SEntry.isFile()) {
1566 const SrcMgr::FileInfo &FI = SEntry.getFile();
1567 if (!FI.hasLineDirectives()) {
1568 if (FI.getFileCharacteristic() == SrcMgr::C_ExternCSystem)
1569 return true;
1570 if (FI.getFileCharacteristic() == SrcMgr::C_System) {
1571 // This file is in a system header directory. Continue with commiting change
1572 // only if it is a user specified system directory because user put a
1573 // .system_framework file in the framework directory.
1574 StringRef Directory(file->getDir()->getName());
1575 size_t Ix = Directory.rfind(".framework");
1576 if (Ix == StringRef::npos)
1577 return true;
1578 std::string PatchToSystemFramework = Directory.slice(0, Ix+sizeof(".framework"));
1579 PatchToSystemFramework += ".system_framework";
1580 if (!llvm::sys::fs::exists(PatchToSystemFramework.data()))
1581 return true;
1582 }
1583 }
1584 }
1585 return false;
1586}
1587
Ted Kremenekf7639e12012-03-06 20:06:33 +00001588void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
Fariborz Jahaniana7437f02013-07-03 23:05:00 +00001589
1590 TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
Fariborz Jahanianc1213862013-10-02 21:32:39 +00001591 if (ASTMigrateActions & FrontendOptions::ObjCMT_MigrateDecls) {
Fariborz Jahaniand83ef842013-07-09 16:59:14 +00001592 for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();
1593 D != DEnd; ++D) {
Fariborz Jahanianb8343522013-08-20 23:35:26 +00001594 if (unsigned FID =
1595 PP.getSourceManager().getFileID((*D)->getLocation()).getHashValue())
Fariborz Jahanian8c45e282013-10-02 22:49:59 +00001596 if (FileId && FileId != FID) {
1597 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
1598 AnnotateImplicitBridging(Ctx);
1599 }
1600
Fariborz Jahaniand83ef842013-07-09 16:59:14 +00001601 if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))
1602 migrateObjCInterfaceDecl(Ctx, CDecl);
Fariborz Jahanian9d2ffea2013-10-31 00:06:58 +00001603 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(*D)) {
Fariborz Jahanian92f72422013-09-17 19:00:30 +00001604 migrateObjCInterfaceDecl(Ctx, CatDecl);
Fariborz Jahanian9d2ffea2013-10-31 00:06:58 +00001605 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
Fariborz Jahaniand41dbad2013-10-31 16:10:44 +00001606 migrateDeprecatedAnnotation(Ctx, CatDecl);
Fariborz Jahanian9d2ffea2013-10-31 00:06:58 +00001607 }
Fariborz Jahanian1be01532013-07-12 22:32:19 +00001608 else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D))
1609 ObjCProtocolDecls.insert(PDecl);
1610 else if (const ObjCImplementationDecl *ImpDecl =
Fariborz Jahanian8c45e282013-10-02 22:49:59 +00001611 dyn_cast<ObjCImplementationDecl>(*D)) {
1612 if (ASTMigrateActions & FrontendOptions::ObjCMT_ProtocolConformance)
1613 migrateProtocolConformance(Ctx, ImpDecl);
1614 }
Fariborz Jahanian92463272013-07-18 20:11:45 +00001615 else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {
Fariborz Jahanian059e05e2013-10-15 00:00:28 +00001616 if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros))
1617 continue;
Fariborz Jahanian92463272013-07-18 20:11:45 +00001618 DeclContext::decl_iterator N = D;
Fariborz Jahanian059e05e2013-10-15 00:00:28 +00001619 if (++N != DEnd) {
1620 const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N);
1621 if (migrateNSEnumDecl(Ctx, ED, TD) && TD)
1622 D++;
1623 }
1624 else
1625 migrateNSEnumDecl(Ctx, ED, /*TypedefDecl */0);
Fariborz Jahanian11dd4b12013-10-11 21:34:56 +00001626 }
1627 else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*D)) {
Fariborz Jahanian403425b2013-10-17 23:13:13 +00001628 if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros))
1629 continue;
1630 DeclContext::decl_iterator N = D;
1631 if (++N == DEnd)
1632 continue;
1633 if (const EnumDecl *ED = dyn_cast<EnumDecl>(*N)) {
Fariborz Jahanian059e05e2013-10-15 00:00:28 +00001634 if (++N != DEnd)
Fariborz Jahanian403425b2013-10-17 23:13:13 +00001635 if (const TypedefDecl *TDF = dyn_cast<TypedefDecl>(*N)) {
1636 // prefer typedef-follows-enum to enum-follows-typedef pattern.
1637 if (migrateNSEnumDecl(Ctx, ED, TDF)) {
1638 ++D; ++D;
1639 CacheObjCNSIntegerTypedefed(TD);
Fariborz Jahanian059e05e2013-10-15 00:00:28 +00001640 continue;
1641 }
Fariborz Jahanian11dd4b12013-10-11 21:34:56 +00001642 }
Fariborz Jahanian403425b2013-10-17 23:13:13 +00001643 if (migrateNSEnumDecl(Ctx, ED, TD)) {
1644 ++D;
1645 continue;
1646 }
Fariborz Jahanian059e05e2013-10-15 00:00:28 +00001647 }
Fariborz Jahanian403425b2013-10-17 23:13:13 +00001648 CacheObjCNSIntegerTypedefed(TD);
Fariborz Jahanian92463272013-07-18 20:11:45 +00001649 }
Fariborz Jahanian8c45e282013-10-02 22:49:59 +00001650 else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D)) {
1651 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
1652 migrateCFAnnotation(Ctx, FD);
1653 }
Fariborz Jahanianc13c1b02013-08-13 18:01:42 +00001654
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001655 if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) {
1656 // migrate methods which can have instancetype as their result type.
Fariborz Jahanian8c45e282013-10-02 22:49:59 +00001657 if (ASTMigrateActions & FrontendOptions::ObjCMT_Instancetype)
1658 migrateAllMethodInstaceType(Ctx, CDecl);
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001659 // annotate methods with CF annotations.
Fariborz Jahanian8c45e282013-10-02 22:49:59 +00001660 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
1661 migrateARCSafeAnnotation(Ctx, CDecl);
Fariborz Jahanian926fafb2013-08-22 18:35:27 +00001662 }
Fariborz Jahaniand83ef842013-07-09 16:59:14 +00001663 }
Fariborz Jahanian8c45e282013-10-02 22:49:59 +00001664 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
1665 AnnotateImplicitBridging(Ctx);
Fariborz Jahanian301b5212013-08-20 22:42:13 +00001666 }
Fariborz Jahaniana7437f02013-07-03 23:05:00 +00001667
David Blaikiebbafb8a2012-03-11 07:00:24 +00001668 Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenekf7639e12012-03-06 20:06:33 +00001669 RewritesReceiver Rec(rewriter);
1670 Editor->applyRewrites(Rec);
1671
1672 for (Rewriter::buffer_iterator
1673 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
1674 FileID FID = I->first;
1675 RewriteBuffer &buf = I->second;
1676 const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
1677 assert(file);
Fariborz Jahanian8f5225b2013-10-01 21:16:29 +00001678 if (IsReallyASystemHeader(Ctx, file, FID))
1679 continue;
Dmitri Gribenkof8579502013-01-12 19:30:44 +00001680 SmallString<512> newText;
Ted Kremenekf7639e12012-03-06 20:06:33 +00001681 llvm::raw_svector_ostream vecOS(newText);
1682 buf.write(vecOS);
1683 vecOS.flush();
1684 llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
1685 StringRef(newText.data(), newText.size()), file->getName());
Dmitri Gribenkof8579502013-01-12 19:30:44 +00001686 SmallString<64> filePath(file->getName());
Ted Kremenekf7639e12012-03-06 20:06:33 +00001687 FileMgr.FixupRelativePath(filePath);
1688 Remapper.remap(filePath.str(), memBuf);
1689 }
1690
1691 if (IsOutputFile) {
1692 Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
1693 } else {
1694 Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
1695 }
1696}
1697
1698bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
Argyrios Kyrtzidisb4822602012-05-24 16:48:23 +00001699 CI.getDiagnostics().setIgnoreAllWarnings(true);
Ted Kremenekf7639e12012-03-06 20:06:33 +00001700 return true;
1701}
1702
1703ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI,
1704 StringRef InFile) {
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +00001705 PPConditionalDirectiveRecord *
1706 PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
1707 CI.getPreprocessor().addPPCallbacks(PPRec);
Ted Kremenekf7639e12012-03-06 20:06:33 +00001708 return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile,
Fariborz Jahanianc1213862013-10-02 21:32:39 +00001709 FrontendOptions::ObjCMT_MigrateAll,
Ted Kremenekf7639e12012-03-06 20:06:33 +00001710 Remapper,
1711 CI.getFileManager(),
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +00001712 PPRec,
Fariborz Jahaniana7437f02013-07-03 23:05:00 +00001713 CI.getPreprocessor(),
Ted Kremenekf7639e12012-03-06 20:06:33 +00001714 /*isOutputFile=*/true);
1715}