150
|
1 //===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===//
|
|
2 //
|
|
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
4 // See https://llvm.org/LICENSE.txt for license information.
|
|
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
6 //
|
|
7 //===----------------------------------------------------------------------===//
|
|
8
|
|
9 #include "Transforms.h"
|
|
10 #include "clang/Analysis/RetainSummaryManager.h"
|
|
11 #include "clang/ARCMigrate/ARCMT.h"
|
|
12 #include "clang/ARCMigrate/ARCMTActions.h"
|
|
13 #include "clang/AST/ASTConsumer.h"
|
|
14 #include "clang/AST/ASTContext.h"
|
|
15 #include "clang/AST/Attr.h"
|
|
16 #include "clang/AST/NSAPI.h"
|
|
17 #include "clang/AST/ParentMap.h"
|
|
18 #include "clang/AST/RecursiveASTVisitor.h"
|
|
19 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
|
|
20 #include "clang/Basic/FileManager.h"
|
|
21 #include "clang/Edit/Commit.h"
|
|
22 #include "clang/Edit/EditedSource.h"
|
|
23 #include "clang/Edit/EditsReceiver.h"
|
|
24 #include "clang/Edit/Rewriters.h"
|
|
25 #include "clang/Frontend/CompilerInstance.h"
|
|
26 #include "clang/Frontend/MultiplexConsumer.h"
|
|
27 #include "clang/Lex/PPConditionalDirectiveRecord.h"
|
|
28 #include "clang/Lex/Preprocessor.h"
|
|
29 #include "clang/Rewrite/Core/Rewriter.h"
|
|
30 #include "llvm/ADT/SmallString.h"
|
|
31 #include "llvm/ADT/StringSet.h"
|
|
32 #include "llvm/Support/Path.h"
|
|
33 #include "llvm/Support/SourceMgr.h"
|
|
34 #include "llvm/Support/YAMLParser.h"
|
|
35
|
|
36 using namespace clang;
|
|
37 using namespace arcmt;
|
|
38 using namespace ento;
|
|
39
|
|
40 namespace {
|
|
41
|
|
42 class ObjCMigrateASTConsumer : public ASTConsumer {
|
|
43 enum CF_BRIDGING_KIND {
|
|
44 CF_BRIDGING_NONE,
|
|
45 CF_BRIDGING_ENABLE,
|
|
46 CF_BRIDGING_MAY_INCLUDE
|
|
47 };
|
|
48
|
|
49 void migrateDecl(Decl *D);
|
|
50 void migrateObjCContainerDecl(ASTContext &Ctx, ObjCContainerDecl *D);
|
|
51 void migrateProtocolConformance(ASTContext &Ctx,
|
|
52 const ObjCImplementationDecl *ImpDecl);
|
|
53 void CacheObjCNSIntegerTypedefed(const TypedefDecl *TypedefDcl);
|
|
54 bool migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl,
|
|
55 const TypedefDecl *TypedefDcl);
|
|
56 void migrateAllMethodInstaceType(ASTContext &Ctx, ObjCContainerDecl *CDecl);
|
|
57 void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl,
|
|
58 ObjCMethodDecl *OM);
|
|
59 bool migrateProperty(ASTContext &Ctx, ObjCContainerDecl *D, ObjCMethodDecl *OM);
|
|
60 void migrateNsReturnsInnerPointer(ASTContext &Ctx, ObjCMethodDecl *OM);
|
|
61 void migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, ObjCPropertyDecl *P);
|
|
62 void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl,
|
|
63 ObjCMethodDecl *OM,
|
|
64 ObjCInstanceTypeFamily OIT_Family = OIT_None);
|
|
65
|
|
66 void migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl);
|
|
67 void AddCFAnnotations(ASTContext &Ctx,
|
|
68 const RetainSummary *RS,
|
|
69 const FunctionDecl *FuncDecl, bool ResultAnnotated);
|
|
70 void AddCFAnnotations(ASTContext &Ctx,
|
|
71 const RetainSummary *RS,
|
|
72 const ObjCMethodDecl *MethodDecl, bool ResultAnnotated);
|
|
73
|
|
74 void AnnotateImplicitBridging(ASTContext &Ctx);
|
|
75
|
|
76 CF_BRIDGING_KIND migrateAddFunctionAnnotation(ASTContext &Ctx,
|
|
77 const FunctionDecl *FuncDecl);
|
|
78
|
|
79 void migrateARCSafeAnnotation(ASTContext &Ctx, ObjCContainerDecl *CDecl);
|
|
80
|
|
81 void migrateAddMethodAnnotation(ASTContext &Ctx,
|
|
82 const ObjCMethodDecl *MethodDecl);
|
|
83
|
|
84 void inferDesignatedInitializers(ASTContext &Ctx,
|
|
85 const ObjCImplementationDecl *ImplD);
|
|
86
|
|
87 bool InsertFoundation(ASTContext &Ctx, SourceLocation Loc);
|
|
88
|
|
89 std::unique_ptr<RetainSummaryManager> Summaries;
|
|
90
|
|
91 public:
|
|
92 std::string MigrateDir;
|
|
93 unsigned ASTMigrateActions;
|
|
94 FileID FileId;
|
|
95 const TypedefDecl *NSIntegerTypedefed;
|
|
96 const TypedefDecl *NSUIntegerTypedefed;
|
|
97 std::unique_ptr<NSAPI> NSAPIObj;
|
|
98 std::unique_ptr<edit::EditedSource> Editor;
|
|
99 FileRemapper &Remapper;
|
|
100 FileManager &FileMgr;
|
|
101 const PPConditionalDirectiveRecord *PPRec;
|
|
102 Preprocessor &PP;
|
|
103 bool IsOutputFile;
|
|
104 bool FoundationIncluded;
|
|
105 llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls;
|
|
106 llvm::SmallVector<const Decl *, 8> CFFunctionIBCandidates;
|
|
107 llvm::StringSet<> WhiteListFilenames;
|
|
108
|
|
109 RetainSummaryManager &getSummaryManager(ASTContext &Ctx) {
|
|
110 if (!Summaries)
|
|
111 Summaries.reset(new RetainSummaryManager(Ctx,
|
|
112 /*TrackNSCFObjects=*/true,
|
|
113 /*trackOSObjects=*/false));
|
|
114 return *Summaries;
|
|
115 }
|
|
116
|
|
117 ObjCMigrateASTConsumer(StringRef migrateDir, unsigned astMigrateActions,
|
|
118 FileRemapper &remapper, FileManager &fileMgr,
|
|
119 const PPConditionalDirectiveRecord *PPRec,
|
|
120 Preprocessor &PP, bool isOutputFile,
|
|
121 ArrayRef<std::string> WhiteList)
|
|
122 : MigrateDir(migrateDir), ASTMigrateActions(astMigrateActions),
|
|
123 NSIntegerTypedefed(nullptr), NSUIntegerTypedefed(nullptr),
|
|
124 Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),
|
|
125 IsOutputFile(isOutputFile), FoundationIncluded(false) {
|
|
126 // FIXME: StringSet should have insert(iter, iter) to use here.
|
|
127 for (const std::string &Val : WhiteList)
|
|
128 WhiteListFilenames.insert(Val);
|
|
129 }
|
|
130
|
|
131 protected:
|
|
132 void Initialize(ASTContext &Context) override {
|
|
133 NSAPIObj.reset(new NSAPI(Context));
|
|
134 Editor.reset(new edit::EditedSource(Context.getSourceManager(),
|
|
135 Context.getLangOpts(),
|
|
136 PPRec));
|
|
137 }
|
|
138
|
|
139 bool HandleTopLevelDecl(DeclGroupRef DG) override {
|
|
140 for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
|
|
141 migrateDecl(*I);
|
|
142 return true;
|
|
143 }
|
|
144 void HandleInterestingDecl(DeclGroupRef DG) override {
|
|
145 // Ignore decls from the PCH.
|
|
146 }
|
|
147 void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override {
|
|
148 ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
|
|
149 }
|
|
150
|
|
151 void HandleTranslationUnit(ASTContext &Ctx) override;
|
|
152
|
|
153 bool canModifyFile(StringRef Path) {
|
|
154 if (WhiteListFilenames.empty())
|
|
155 return true;
|
|
156 return WhiteListFilenames.find(llvm::sys::path::filename(Path))
|
|
157 != WhiteListFilenames.end();
|
|
158 }
|
|
159 bool canModifyFile(const FileEntry *FE) {
|
|
160 if (!FE)
|
|
161 return false;
|
|
162 return canModifyFile(FE->getName());
|
|
163 }
|
|
164 bool canModifyFile(FileID FID) {
|
|
165 if (FID.isInvalid())
|
|
166 return false;
|
|
167 return canModifyFile(PP.getSourceManager().getFileEntryForID(FID));
|
|
168 }
|
|
169
|
|
170 bool canModify(const Decl *D) {
|
|
171 if (!D)
|
|
172 return false;
|
|
173 if (const ObjCCategoryImplDecl *CatImpl = dyn_cast<ObjCCategoryImplDecl>(D))
|
|
174 return canModify(CatImpl->getCategoryDecl());
|
|
175 if (const ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(D))
|
|
176 return canModify(Impl->getClassInterface());
|
|
177 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
|
|
178 return canModify(cast<Decl>(MD->getDeclContext()));
|
|
179
|
|
180 FileID FID = PP.getSourceManager().getFileID(D->getLocation());
|
|
181 return canModifyFile(FID);
|
|
182 }
|
|
183 };
|
|
184
|
|
185 } // end anonymous namespace
|
|
186
|
|
187 ObjCMigrateAction::ObjCMigrateAction(
|
|
188 std::unique_ptr<FrontendAction> WrappedAction, StringRef migrateDir,
|
|
189 unsigned migrateAction)
|
|
190 : WrapperFrontendAction(std::move(WrappedAction)), MigrateDir(migrateDir),
|
|
191 ObjCMigAction(migrateAction), CompInst(nullptr) {
|
|
192 if (MigrateDir.empty())
|
|
193 MigrateDir = "."; // user current directory if none is given.
|
|
194 }
|
|
195
|
|
196 std::unique_ptr<ASTConsumer>
|
|
197 ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
|
|
198 PPConditionalDirectiveRecord *
|
|
199 PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
|
|
200 CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec));
|
|
201 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
|
|
202 Consumers.push_back(WrapperFrontendAction::CreateASTConsumer(CI, InFile));
|
|
203 Consumers.push_back(std::make_unique<ObjCMigrateASTConsumer>(
|
|
204 MigrateDir, ObjCMigAction, Remapper, CompInst->getFileManager(), PPRec,
|
|
205 CompInst->getPreprocessor(), false, None));
|
|
206 return std::make_unique<MultiplexConsumer>(std::move(Consumers));
|
|
207 }
|
|
208
|
|
209 bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
|
|
210 Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
|
|
211 /*ignoreIfFilesChanged=*/true);
|
|
212 CompInst = &CI;
|
|
213 CI.getDiagnostics().setIgnoreAllWarnings(true);
|
|
214 return true;
|
|
215 }
|
|
216
|
|
217 namespace {
|
|
218 // FIXME. This duplicates one in RewriteObjCFoundationAPI.cpp
|
|
219 bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
|
|
220 const Expr* Expr = FullExpr->IgnoreImpCasts();
|
|
221 return !(isa<ArraySubscriptExpr>(Expr) || isa<CallExpr>(Expr) ||
|
|
222 isa<DeclRefExpr>(Expr) || isa<CXXNamedCastExpr>(Expr) ||
|
|
223 isa<CXXConstructExpr>(Expr) || isa<CXXThisExpr>(Expr) ||
|
|
224 isa<CXXTypeidExpr>(Expr) ||
|
|
225 isa<CXXUnresolvedConstructExpr>(Expr) ||
|
|
226 isa<ObjCMessageExpr>(Expr) || isa<ObjCPropertyRefExpr>(Expr) ||
|
|
227 isa<ObjCProtocolExpr>(Expr) || isa<MemberExpr>(Expr) ||
|
|
228 isa<ObjCIvarRefExpr>(Expr) || isa<ParenExpr>(FullExpr) ||
|
|
229 isa<ParenListExpr>(Expr) || isa<SizeOfPackExpr>(Expr));
|
|
230 }
|
|
231
|
|
232 /// - Rewrite message expression for Objective-C setter and getters into
|
|
233 /// property-dot syntax.
|
|
234 bool rewriteToPropertyDotSyntax(const ObjCMessageExpr *Msg,
|
|
235 Preprocessor &PP,
|
|
236 const NSAPI &NS, edit::Commit &commit,
|
|
237 const ParentMap *PMap) {
|
|
238 if (!Msg || Msg->isImplicit() ||
|
|
239 (Msg->getReceiverKind() != ObjCMessageExpr::Instance &&
|
|
240 Msg->getReceiverKind() != ObjCMessageExpr::SuperInstance))
|
|
241 return false;
|
|
242 if (const Expr *Receiver = Msg->getInstanceReceiver())
|
|
243 if (Receiver->getType()->isObjCBuiltinType())
|
|
244 return false;
|
|
245
|
|
246 const ObjCMethodDecl *Method = Msg->getMethodDecl();
|
|
247 if (!Method)
|
|
248 return false;
|
|
249 if (!Method->isPropertyAccessor())
|
|
250 return false;
|
|
251
|
|
252 const ObjCPropertyDecl *Prop = Method->findPropertyDecl();
|
|
253 if (!Prop)
|
|
254 return false;
|
|
255
|
|
256 SourceRange MsgRange = Msg->getSourceRange();
|
|
257 bool ReceiverIsSuper =
|
|
258 (Msg->getReceiverKind() == ObjCMessageExpr::SuperInstance);
|
|
259 // for 'super' receiver is nullptr.
|
|
260 const Expr *receiver = Msg->getInstanceReceiver();
|
|
261 bool NeedsParen =
|
|
262 ReceiverIsSuper ? false : subscriptOperatorNeedsParens(receiver);
|
|
263 bool IsGetter = (Msg->getNumArgs() == 0);
|
|
264 if (IsGetter) {
|
|
265 // Find space location range between receiver expression and getter method.
|
|
266 SourceLocation BegLoc =
|
|
267 ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getEndLoc();
|
|
268 BegLoc = PP.getLocForEndOfToken(BegLoc);
|
|
269 SourceLocation EndLoc = Msg->getSelectorLoc(0);
|
|
270 SourceRange SpaceRange(BegLoc, EndLoc);
|
|
271 std::string PropertyDotString;
|
|
272 // rewrite getter method expression into: receiver.property or
|
|
273 // (receiver).property
|
|
274 if (NeedsParen) {
|
|
275 commit.insertBefore(receiver->getBeginLoc(), "(");
|
|
276 PropertyDotString = ").";
|
|
277 }
|
|
278 else
|
|
279 PropertyDotString = ".";
|
|
280 PropertyDotString += Prop->getName();
|
|
281 commit.replace(SpaceRange, PropertyDotString);
|
|
282
|
|
283 // remove '[' ']'
|
|
284 commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), "");
|
|
285 commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), "");
|
|
286 } else {
|
|
287 if (NeedsParen)
|
|
288 commit.insertWrap("(", receiver->getSourceRange(), ")");
|
|
289 std::string PropertyDotString = ".";
|
|
290 PropertyDotString += Prop->getName();
|
|
291 PropertyDotString += " =";
|
|
292 const Expr*const* Args = Msg->getArgs();
|
|
293 const Expr *RHS = Args[0];
|
|
294 if (!RHS)
|
|
295 return false;
|
|
296 SourceLocation BegLoc =
|
|
297 ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getEndLoc();
|
|
298 BegLoc = PP.getLocForEndOfToken(BegLoc);
|
|
299 SourceLocation EndLoc = RHS->getBeginLoc();
|
|
300 EndLoc = EndLoc.getLocWithOffset(-1);
|
|
301 const char *colon = PP.getSourceManager().getCharacterData(EndLoc);
|
|
302 // Add a space after '=' if there is no space between RHS and '='
|
|
303 if (colon && colon[0] == ':')
|
|
304 PropertyDotString += " ";
|
|
305 SourceRange Range(BegLoc, EndLoc);
|
|
306 commit.replace(Range, PropertyDotString);
|
|
307 // remove '[' ']'
|
|
308 commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), "");
|
|
309 commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), "");
|
|
310 }
|
|
311 return true;
|
|
312 }
|
|
313
|
|
314 class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
|
|
315 ObjCMigrateASTConsumer &Consumer;
|
|
316 ParentMap &PMap;
|
|
317
|
|
318 public:
|
|
319 ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
|
|
320 : Consumer(consumer), PMap(PMap) { }
|
|
321
|
|
322 bool shouldVisitTemplateInstantiations() const { return false; }
|
|
323 bool shouldWalkTypesOfTypeLocs() const { return false; }
|
|
324
|
|
325 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
|
|
326 if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Literals) {
|
|
327 edit::Commit commit(*Consumer.Editor);
|
|
328 edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
|
|
329 Consumer.Editor->commit(commit);
|
|
330 }
|
|
331
|
|
332 if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Subscripting) {
|
|
333 edit::Commit commit(*Consumer.Editor);
|
|
334 edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
|
|
335 Consumer.Editor->commit(commit);
|
|
336 }
|
|
337
|
|
338 if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_PropertyDotSyntax) {
|
|
339 edit::Commit commit(*Consumer.Editor);
|
|
340 rewriteToPropertyDotSyntax(E, Consumer.PP, *Consumer.NSAPIObj,
|
|
341 commit, &PMap);
|
|
342 Consumer.Editor->commit(commit);
|
|
343 }
|
|
344
|
|
345 return true;
|
|
346 }
|
|
347
|
|
348 bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
|
|
349 // Do depth first; we want to rewrite the subexpressions first so that if
|
|
350 // we have to move expressions we will move them already rewritten.
|
|
351 for (Stmt *SubStmt : E->children())
|
|
352 if (!TraverseStmt(SubStmt))
|
|
353 return false;
|
|
354
|
|
355 return WalkUpFromObjCMessageExpr(E);
|
|
356 }
|
|
357 };
|
|
358
|
|
359 class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
|
|
360 ObjCMigrateASTConsumer &Consumer;
|
|
361 std::unique_ptr<ParentMap> PMap;
|
|
362
|
|
363 public:
|
|
364 BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
|
|
365
|
|
366 bool shouldVisitTemplateInstantiations() const { return false; }
|
|
367 bool shouldWalkTypesOfTypeLocs() const { return false; }
|
|
368
|
|
369 bool TraverseStmt(Stmt *S) {
|
|
370 PMap.reset(new ParentMap(S));
|
|
371 ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
|
|
372 return true;
|
|
373 }
|
|
374 };
|
|
375 } // end anonymous namespace
|
|
376
|
|
377 void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
|
|
378 if (!D)
|
|
379 return;
|
|
380 if (isa<ObjCMethodDecl>(D))
|
|
381 return; // Wait for the ObjC container declaration.
|
|
382
|
|
383 BodyMigrator(*this).TraverseDecl(D);
|
|
384 }
|
|
385
|
|
386 static void append_attr(std::string &PropertyString, const char *attr,
|
|
387 bool &LParenAdded) {
|
|
388 if (!LParenAdded) {
|
|
389 PropertyString += "(";
|
|
390 LParenAdded = true;
|
|
391 }
|
|
392 else
|
|
393 PropertyString += ", ";
|
|
394 PropertyString += attr;
|
|
395 }
|
|
396
|
|
397 static
|
|
398 void MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString,
|
|
399 const std::string& TypeString,
|
|
400 const char *name) {
|
|
401 const char *argPtr = TypeString.c_str();
|
|
402 int paren = 0;
|
|
403 while (*argPtr) {
|
|
404 switch (*argPtr) {
|
|
405 case '(':
|
|
406 PropertyString += *argPtr;
|
|
407 paren++;
|
|
408 break;
|
|
409 case ')':
|
|
410 PropertyString += *argPtr;
|
|
411 paren--;
|
|
412 break;
|
|
413 case '^':
|
|
414 case '*':
|
|
415 PropertyString += (*argPtr);
|
|
416 if (paren == 1) {
|
|
417 PropertyString += name;
|
|
418 name = "";
|
|
419 }
|
|
420 break;
|
|
421 default:
|
|
422 PropertyString += *argPtr;
|
|
423 break;
|
|
424 }
|
|
425 argPtr++;
|
|
426 }
|
|
427 }
|
|
428
|
|
429 static const char *PropertyMemoryAttribute(ASTContext &Context, QualType ArgType) {
|
|
430 Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();
|
|
431 bool RetainableObject = ArgType->isObjCRetainableType();
|
|
432 if (RetainableObject &&
|
|
433 (propertyLifetime == Qualifiers::OCL_Strong
|
|
434 || propertyLifetime == Qualifiers::OCL_None)) {
|
|
435 if (const ObjCObjectPointerType *ObjPtrTy =
|
|
436 ArgType->getAs<ObjCObjectPointerType>()) {
|
|
437 ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
|
|
438 if (IDecl &&
|
|
439 IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying")))
|
|
440 return "copy";
|
|
441 else
|
|
442 return "strong";
|
|
443 }
|
|
444 else if (ArgType->isBlockPointerType())
|
|
445 return "copy";
|
|
446 } else if (propertyLifetime == Qualifiers::OCL_Weak)
|
|
447 // TODO. More precise determination of 'weak' attribute requires
|
|
448 // looking into setter's implementation for backing weak ivar.
|
|
449 return "weak";
|
|
450 else if (RetainableObject)
|
|
451 return ArgType->isBlockPointerType() ? "copy" : "strong";
|
|
452 return nullptr;
|
|
453 }
|
|
454
|
|
455 static void rewriteToObjCProperty(const ObjCMethodDecl *Getter,
|
|
456 const ObjCMethodDecl *Setter,
|
|
457 const NSAPI &NS, edit::Commit &commit,
|
|
458 unsigned LengthOfPrefix,
|
|
459 bool Atomic, bool UseNsIosOnlyMacro,
|
|
460 bool AvailabilityArgsMatch) {
|
|
461 ASTContext &Context = NS.getASTContext();
|
|
462 bool LParenAdded = false;
|
|
463 std::string PropertyString = "@property ";
|
|
464 if (UseNsIosOnlyMacro && NS.isMacroDefined("NS_NONATOMIC_IOSONLY")) {
|
|
465 PropertyString += "(NS_NONATOMIC_IOSONLY";
|
|
466 LParenAdded = true;
|
|
467 } else if (!Atomic) {
|
|
468 PropertyString += "(nonatomic";
|
|
469 LParenAdded = true;
|
|
470 }
|
|
471
|
|
472 std::string PropertyNameString = Getter->getNameAsString();
|
|
473 StringRef PropertyName(PropertyNameString);
|
|
474 if (LengthOfPrefix > 0) {
|
|
475 if (!LParenAdded) {
|
|
476 PropertyString += "(getter=";
|
|
477 LParenAdded = true;
|
|
478 }
|
|
479 else
|
|
480 PropertyString += ", getter=";
|
|
481 PropertyString += PropertyNameString;
|
|
482 }
|
|
483 // Property with no setter may be suggested as a 'readonly' property.
|
|
484 if (!Setter)
|
|
485 append_attr(PropertyString, "readonly", LParenAdded);
|
|
486
|
|
487
|
|
488 // Short circuit 'delegate' properties that contain the name "delegate" or
|
|
489 // "dataSource", or have exact name "target" to have 'assign' attribute.
|
|
490 if (PropertyName.equals("target") ||
|
|
491 (PropertyName.find("delegate") != StringRef::npos) ||
|
|
492 (PropertyName.find("dataSource") != StringRef::npos)) {
|
|
493 QualType QT = Getter->getReturnType();
|
|
494 if (!QT->isRealType())
|
|
495 append_attr(PropertyString, "assign", LParenAdded);
|
|
496 } else if (!Setter) {
|
|
497 QualType ResType = Context.getCanonicalType(Getter->getReturnType());
|
|
498 if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ResType))
|
|
499 append_attr(PropertyString, MemoryManagementAttr, LParenAdded);
|
|
500 } else {
|
|
501 const ParmVarDecl *argDecl = *Setter->param_begin();
|
|
502 QualType ArgType = Context.getCanonicalType(argDecl->getType());
|
|
503 if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ArgType))
|
|
504 append_attr(PropertyString, MemoryManagementAttr, LParenAdded);
|
|
505 }
|
|
506 if (LParenAdded)
|
|
507 PropertyString += ')';
|
|
508 QualType RT = Getter->getReturnType();
|
|
509 if (!isa<TypedefType>(RT)) {
|
|
510 // strip off any ARC lifetime qualifier.
|
|
511 QualType CanResultTy = Context.getCanonicalType(RT);
|
|
512 if (CanResultTy.getQualifiers().hasObjCLifetime()) {
|
|
513 Qualifiers Qs = CanResultTy.getQualifiers();
|
|
514 Qs.removeObjCLifetime();
|
|
515 RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs);
|
|
516 }
|
|
517 }
|
|
518 PropertyString += " ";
|
|
519 PrintingPolicy SubPolicy(Context.getPrintingPolicy());
|
|
520 SubPolicy.SuppressStrongLifetime = true;
|
|
521 SubPolicy.SuppressLifetimeQualifiers = true;
|
|
522 std::string TypeString = RT.getAsString(SubPolicy);
|
|
523 if (LengthOfPrefix > 0) {
|
|
524 // property name must strip off "is" and lower case the first character
|
|
525 // after that; e.g. isContinuous will become continuous.
|
|
526 StringRef PropertyNameStringRef(PropertyNameString);
|
|
527 PropertyNameStringRef = PropertyNameStringRef.drop_front(LengthOfPrefix);
|
|
528 PropertyNameString = std::string(PropertyNameStringRef);
|
|
529 bool NoLowering = (isUppercase(PropertyNameString[0]) &&
|
|
530 PropertyNameString.size() > 1 &&
|
|
531 isUppercase(PropertyNameString[1]));
|
|
532 if (!NoLowering)
|
|
533 PropertyNameString[0] = toLowercase(PropertyNameString[0]);
|
|
534 }
|
|
535 if (RT->isBlockPointerType() || RT->isFunctionPointerType())
|
|
536 MigrateBlockOrFunctionPointerTypeVariable(PropertyString,
|
|
537 TypeString,
|
|
538 PropertyNameString.c_str());
|
|
539 else {
|
|
540 char LastChar = TypeString[TypeString.size()-1];
|
|
541 PropertyString += TypeString;
|
|
542 if (LastChar != '*')
|
|
543 PropertyString += ' ';
|
|
544 PropertyString += PropertyNameString;
|
|
545 }
|
|
546 SourceLocation StartGetterSelectorLoc = Getter->getSelectorStartLoc();
|
|
547 Selector GetterSelector = Getter->getSelector();
|
|
548
|
|
549 SourceLocation EndGetterSelectorLoc =
|
|
550 StartGetterSelectorLoc.getLocWithOffset(GetterSelector.getNameForSlot(0).size());
|
|
551 commit.replace(CharSourceRange::getCharRange(Getter->getBeginLoc(),
|
|
552 EndGetterSelectorLoc),
|
|
553 PropertyString);
|
|
554 if (Setter && AvailabilityArgsMatch) {
|
|
555 SourceLocation EndLoc = Setter->getDeclaratorEndLoc();
|
|
556 // Get location past ';'
|
|
557 EndLoc = EndLoc.getLocWithOffset(1);
|
|
558 SourceLocation BeginOfSetterDclLoc = Setter->getBeginLoc();
|
|
559 // FIXME. This assumes that setter decl; is immediately preceded by eoln.
|
|
560 // It is trying to remove the setter method decl. line entirely.
|
|
561 BeginOfSetterDclLoc = BeginOfSetterDclLoc.getLocWithOffset(-1);
|
|
562 commit.remove(SourceRange(BeginOfSetterDclLoc, EndLoc));
|
|
563 }
|
|
564 }
|
|
565
|
|
566 static bool IsCategoryNameWithDeprecatedSuffix(ObjCContainerDecl *D) {
|
|
567 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(D)) {
|
|
568 StringRef Name = CatDecl->getName();
|
|
569 return Name.endswith("Deprecated");
|
|
570 }
|
|
571 return false;
|
|
572 }
|
|
573
|
|
574 void ObjCMigrateASTConsumer::migrateObjCContainerDecl(ASTContext &Ctx,
|
|
575 ObjCContainerDecl *D) {
|
|
576 if (D->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(D))
|
|
577 return;
|
|
578
|
|
579 for (auto *Method : D->methods()) {
|
|
580 if (Method->isDeprecated())
|
|
581 continue;
|
|
582 bool PropertyInferred = migrateProperty(Ctx, D, Method);
|
|
583 // If a property is inferred, do not attempt to attach NS_RETURNS_INNER_POINTER to
|
|
584 // the getter method as it ends up on the property itself which we don't want
|
|
585 // to do unless -objcmt-returns-innerpointer-property option is on.
|
|
586 if (!PropertyInferred ||
|
|
587 (ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty))
|
|
588 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
|
|
589 migrateNsReturnsInnerPointer(Ctx, Method);
|
|
590 }
|
|
591 if (!(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty))
|
|
592 return;
|
|
593
|
|
594 for (auto *Prop : D->instance_properties()) {
|
|
595 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
|
|
596 !Prop->isDeprecated())
|
|
597 migratePropertyNsReturnsInnerPointer(Ctx, Prop);
|
|
598 }
|
|
599 }
|
|
600
|
|
601 static bool
|
|
602 ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
|
|
603 const ObjCImplementationDecl *ImpDecl,
|
|
604 const ObjCInterfaceDecl *IDecl,
|
|
605 ObjCProtocolDecl *Protocol) {
|
|
606 // In auto-synthesis, protocol properties are not synthesized. So,
|
|
607 // a conforming protocol must have its required properties declared
|
|
608 // in class interface.
|
|
609 bool HasAtleastOneRequiredProperty = false;
|
|
610 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition())
|
|
611 for (const auto *Property : PDecl->instance_properties()) {
|
|
612 if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional)
|
|
613 continue;
|
|
614 HasAtleastOneRequiredProperty = true;
|
|
615 DeclContext::lookup_result R = IDecl->lookup(Property->getDeclName());
|
|
616 if (R.size() == 0) {
|
|
617 // Relax the rule and look into class's implementation for a synthesize
|
|
618 // or dynamic declaration. Class is implementing a property coming from
|
|
619 // another protocol. This still makes the target protocol as conforming.
|
|
620 if (!ImpDecl->FindPropertyImplDecl(
|
|
621 Property->getDeclName().getAsIdentifierInfo(),
|
|
622 Property->getQueryKind()))
|
|
623 return false;
|
|
624 }
|
|
625 else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) {
|
|
626 if ((ClassProperty->getPropertyAttributes()
|
|
627 != Property->getPropertyAttributes()) ||
|
|
628 !Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
|
|
629 return false;
|
|
630 }
|
|
631 else
|
|
632 return false;
|
|
633 }
|
|
634
|
|
635 // At this point, all required properties in this protocol conform to those
|
|
636 // declared in the class.
|
|
637 // Check that class implements the required methods of the protocol too.
|
|
638 bool HasAtleastOneRequiredMethod = false;
|
|
639 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) {
|
|
640 if (PDecl->meth_begin() == PDecl->meth_end())
|
|
641 return HasAtleastOneRequiredProperty;
|
|
642 for (const auto *MD : PDecl->methods()) {
|
|
643 if (MD->isImplicit())
|
|
644 continue;
|
|
645 if (MD->getImplementationControl() == ObjCMethodDecl::Optional)
|
|
646 continue;
|
|
647 DeclContext::lookup_result R = ImpDecl->lookup(MD->getDeclName());
|
|
648 if (R.size() == 0)
|
|
649 return false;
|
|
650 bool match = false;
|
|
651 HasAtleastOneRequiredMethod = true;
|
|
652 for (unsigned I = 0, N = R.size(); I != N; ++I)
|
|
653 if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0]))
|
|
654 if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {
|
|
655 match = true;
|
|
656 break;
|
|
657 }
|
|
658 if (!match)
|
|
659 return false;
|
|
660 }
|
|
661 }
|
|
662 return HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod;
|
|
663 }
|
|
664
|
|
665 static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl,
|
|
666 llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols,
|
|
667 const NSAPI &NS, edit::Commit &commit) {
|
|
668 const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols();
|
|
669 std::string ClassString;
|
|
670 SourceLocation EndLoc =
|
|
671 IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation();
|
|
672
|
|
673 if (Protocols.empty()) {
|
|
674 ClassString = '<';
|
|
675 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
|
|
676 ClassString += ConformingProtocols[i]->getNameAsString();
|
|
677 if (i != (e-1))
|
|
678 ClassString += ", ";
|
|
679 }
|
|
680 ClassString += "> ";
|
|
681 }
|
|
682 else {
|
|
683 ClassString = ", ";
|
|
684 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
|
|
685 ClassString += ConformingProtocols[i]->getNameAsString();
|
|
686 if (i != (e-1))
|
|
687 ClassString += ", ";
|
|
688 }
|
|
689 ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1;
|
|
690 EndLoc = *PL;
|
|
691 }
|
|
692
|
|
693 commit.insertAfterToken(EndLoc, ClassString);
|
|
694 return true;
|
|
695 }
|
|
696
|
|
697 static StringRef GetUnsignedName(StringRef NSIntegerName) {
|
|
698 StringRef UnsignedName = llvm::StringSwitch<StringRef>(NSIntegerName)
|
|
699 .Case("int8_t", "uint8_t")
|
|
700 .Case("int16_t", "uint16_t")
|
|
701 .Case("int32_t", "uint32_t")
|
|
702 .Case("NSInteger", "NSUInteger")
|
|
703 .Case("int64_t", "uint64_t")
|
|
704 .Default(NSIntegerName);
|
|
705 return UnsignedName;
|
|
706 }
|
|
707
|
|
708 static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,
|
|
709 const TypedefDecl *TypedefDcl,
|
|
710 const NSAPI &NS, edit::Commit &commit,
|
|
711 StringRef NSIntegerName,
|
|
712 bool NSOptions) {
|
|
713 std::string ClassString;
|
|
714 if (NSOptions) {
|
|
715 ClassString = "typedef NS_OPTIONS(";
|
|
716 ClassString += GetUnsignedName(NSIntegerName);
|
|
717 }
|
|
718 else {
|
|
719 ClassString = "typedef NS_ENUM(";
|
|
720 ClassString += NSIntegerName;
|
|
721 }
|
|
722 ClassString += ", ";
|
|
723
|
|
724 ClassString += TypedefDcl->getIdentifier()->getName();
|
|
725 ClassString += ')';
|
|
726 SourceRange R(EnumDcl->getBeginLoc(), EnumDcl->getBeginLoc());
|
|
727 commit.replace(R, ClassString);
|
|
728 SourceLocation EndOfEnumDclLoc = EnumDcl->getEndLoc();
|
|
729 EndOfEnumDclLoc = trans::findSemiAfterLocation(EndOfEnumDclLoc,
|
|
730 NS.getASTContext(), /*IsDecl*/true);
|
|
731 if (EndOfEnumDclLoc.isValid()) {
|
|
732 SourceRange EnumDclRange(EnumDcl->getBeginLoc(), EndOfEnumDclLoc);
|
|
733 commit.insertFromRange(TypedefDcl->getBeginLoc(), EnumDclRange);
|
|
734 }
|
|
735 else
|
|
736 return false;
|
|
737
|
|
738 SourceLocation EndTypedefDclLoc = TypedefDcl->getEndLoc();
|
|
739 EndTypedefDclLoc = trans::findSemiAfterLocation(EndTypedefDclLoc,
|
|
740 NS.getASTContext(), /*IsDecl*/true);
|
|
741 if (EndTypedefDclLoc.isValid()) {
|
|
742 SourceRange TDRange(TypedefDcl->getBeginLoc(), EndTypedefDclLoc);
|
|
743 commit.remove(TDRange);
|
|
744 }
|
|
745 else
|
|
746 return false;
|
|
747
|
|
748 EndOfEnumDclLoc =
|
|
749 trans::findLocationAfterSemi(EnumDcl->getEndLoc(), NS.getASTContext(),
|
|
750 /*IsDecl*/ true);
|
|
751 if (EndOfEnumDclLoc.isValid()) {
|
|
752 SourceLocation BeginOfEnumDclLoc = EnumDcl->getBeginLoc();
|
|
753 // FIXME. This assumes that enum decl; is immediately preceded by eoln.
|
|
754 // It is trying to remove the enum decl. lines entirely.
|
|
755 BeginOfEnumDclLoc = BeginOfEnumDclLoc.getLocWithOffset(-1);
|
|
756 commit.remove(SourceRange(BeginOfEnumDclLoc, EndOfEnumDclLoc));
|
|
757 return true;
|
|
758 }
|
|
759 return false;
|
|
760 }
|
|
761
|
|
762 static void rewriteToNSMacroDecl(ASTContext &Ctx,
|
|
763 const EnumDecl *EnumDcl,
|
|
764 const TypedefDecl *TypedefDcl,
|
|
765 const NSAPI &NS, edit::Commit &commit,
|
|
766 bool IsNSIntegerType) {
|
|
767 QualType DesignatedEnumType = EnumDcl->getIntegerType();
|
|
768 assert(!DesignatedEnumType.isNull()
|
|
769 && "rewriteToNSMacroDecl - underlying enum type is null");
|
|
770
|
|
771 PrintingPolicy Policy(Ctx.getPrintingPolicy());
|
|
772 std::string TypeString = DesignatedEnumType.getAsString(Policy);
|
|
773 std::string ClassString = IsNSIntegerType ? "NS_ENUM(" : "NS_OPTIONS(";
|
|
774 ClassString += TypeString;
|
|
775 ClassString += ", ";
|
|
776
|
|
777 ClassString += TypedefDcl->getIdentifier()->getName();
|
|
778 ClassString += ") ";
|
|
779 SourceLocation EndLoc = EnumDcl->getBraceRange().getBegin();
|
|
780 if (EndLoc.isInvalid())
|
|
781 return;
|
|
782 CharSourceRange R =
|
|
783 CharSourceRange::getCharRange(EnumDcl->getBeginLoc(), EndLoc);
|
|
784 commit.replace(R, ClassString);
|
|
785 // This is to remove spaces between '}' and typedef name.
|
|
786 SourceLocation StartTypedefLoc = EnumDcl->getEndLoc();
|
|
787 StartTypedefLoc = StartTypedefLoc.getLocWithOffset(+1);
|
|
788 SourceLocation EndTypedefLoc = TypedefDcl->getEndLoc();
|
|
789
|
|
790 commit.remove(SourceRange(StartTypedefLoc, EndTypedefLoc));
|
|
791 }
|
|
792
|
|
793 static bool UseNSOptionsMacro(Preprocessor &PP, ASTContext &Ctx,
|
|
794 const EnumDecl *EnumDcl) {
|
|
795 bool PowerOfTwo = true;
|
|
796 bool AllHexdecimalEnumerator = true;
|
|
797 uint64_t MaxPowerOfTwoVal = 0;
|
|
798 for (auto Enumerator : EnumDcl->enumerators()) {
|
|
799 const Expr *InitExpr = Enumerator->getInitExpr();
|
|
800 if (!InitExpr) {
|
|
801 PowerOfTwo = false;
|
|
802 AllHexdecimalEnumerator = false;
|
|
803 continue;
|
|
804 }
|
|
805 InitExpr = InitExpr->IgnoreParenCasts();
|
|
806 if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
|
|
807 if (BO->isShiftOp() || BO->isBitwiseOp())
|
|
808 return true;
|
|
809
|
|
810 uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
|
|
811 if (PowerOfTwo && EnumVal) {
|
|
812 if (!llvm::isPowerOf2_64(EnumVal))
|
|
813 PowerOfTwo = false;
|
|
814 else if (EnumVal > MaxPowerOfTwoVal)
|
|
815 MaxPowerOfTwoVal = EnumVal;
|
|
816 }
|
|
817 if (AllHexdecimalEnumerator && EnumVal) {
|
|
818 bool FoundHexdecimalEnumerator = false;
|
|
819 SourceLocation EndLoc = Enumerator->getEndLoc();
|
|
820 Token Tok;
|
|
821 if (!PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true))
|
|
822 if (Tok.isLiteral() && Tok.getLength() > 2) {
|
|
823 if (const char *StringLit = Tok.getLiteralData())
|
|
824 FoundHexdecimalEnumerator =
|
|
825 (StringLit[0] == '0' && (toLowercase(StringLit[1]) == 'x'));
|
|
826 }
|
|
827 if (!FoundHexdecimalEnumerator)
|
|
828 AllHexdecimalEnumerator = false;
|
|
829 }
|
|
830 }
|
|
831 return AllHexdecimalEnumerator || (PowerOfTwo && (MaxPowerOfTwoVal > 2));
|
|
832 }
|
|
833
|
|
834 void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,
|
|
835 const ObjCImplementationDecl *ImpDecl) {
|
|
836 const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();
|
|
837 if (!IDecl || ObjCProtocolDecls.empty() || IDecl->isDeprecated())
|
|
838 return;
|
|
839 // Find all implicit conforming protocols for this class
|
|
840 // and make them explicit.
|
|
841 llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols;
|
|
842 Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols);
|
|
843 llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols;
|
|
844
|
|
845 for (ObjCProtocolDecl *ProtDecl : ObjCProtocolDecls)
|
|
846 if (!ExplicitProtocols.count(ProtDecl))
|
|
847 PotentialImplicitProtocols.push_back(ProtDecl);
|
|
848
|
|
849 if (PotentialImplicitProtocols.empty())
|
|
850 return;
|
|
851
|
|
852 // go through list of non-optional methods and properties in each protocol
|
|
853 // in the PotentialImplicitProtocols list. If class implements every one of the
|
|
854 // methods and properties, then this class conforms to this protocol.
|
|
855 llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols;
|
|
856 for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)
|
|
857 if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl,
|
|
858 PotentialImplicitProtocols[i]))
|
|
859 ConformingProtocols.push_back(PotentialImplicitProtocols[i]);
|
|
860
|
|
861 if (ConformingProtocols.empty())
|
|
862 return;
|
|
863
|
|
864 // Further reduce number of conforming protocols. If protocol P1 is in the list
|
|
865 // protocol P2 (P2<P1>), No need to include P1.
|
|
866 llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols;
|
|
867 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
|
|
868 bool DropIt = false;
|
|
869 ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i];
|
|
870 for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {
|
|
871 ObjCProtocolDecl *PDecl = ConformingProtocols[i1];
|
|
872 if (PDecl == TargetPDecl)
|
|
873 continue;
|
|
874 if (PDecl->lookupProtocolNamed(
|
|
875 TargetPDecl->getDeclName().getAsIdentifierInfo())) {
|
|
876 DropIt = true;
|
|
877 break;
|
|
878 }
|
|
879 }
|
|
880 if (!DropIt)
|
|
881 MinimalConformingProtocols.push_back(TargetPDecl);
|
|
882 }
|
|
883 if (MinimalConformingProtocols.empty())
|
|
884 return;
|
|
885 edit::Commit commit(*Editor);
|
|
886 rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols,
|
|
887 *NSAPIObj, commit);
|
|
888 Editor->commit(commit);
|
|
889 }
|
|
890
|
|
891 void ObjCMigrateASTConsumer::CacheObjCNSIntegerTypedefed(
|
|
892 const TypedefDecl *TypedefDcl) {
|
|
893
|
|
894 QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
|
|
895 if (NSAPIObj->isObjCNSIntegerType(qt))
|
|
896 NSIntegerTypedefed = TypedefDcl;
|
|
897 else if (NSAPIObj->isObjCNSUIntegerType(qt))
|
|
898 NSUIntegerTypedefed = TypedefDcl;
|
|
899 }
|
|
900
|
|
901 bool ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx,
|
|
902 const EnumDecl *EnumDcl,
|
|
903 const TypedefDecl *TypedefDcl) {
|
|
904 if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() ||
|
|
905 EnumDcl->isDeprecated())
|
|
906 return false;
|
|
907 if (!TypedefDcl) {
|
|
908 if (NSIntegerTypedefed) {
|
|
909 TypedefDcl = NSIntegerTypedefed;
|
|
910 NSIntegerTypedefed = nullptr;
|
|
911 }
|
|
912 else if (NSUIntegerTypedefed) {
|
|
913 TypedefDcl = NSUIntegerTypedefed;
|
|
914 NSUIntegerTypedefed = nullptr;
|
|
915 }
|
|
916 else
|
|
917 return false;
|
|
918 FileID FileIdOfTypedefDcl =
|
|
919 PP.getSourceManager().getFileID(TypedefDcl->getLocation());
|
|
920 FileID FileIdOfEnumDcl =
|
|
921 PP.getSourceManager().getFileID(EnumDcl->getLocation());
|
|
922 if (FileIdOfTypedefDcl != FileIdOfEnumDcl)
|
|
923 return false;
|
|
924 }
|
|
925 if (TypedefDcl->isDeprecated())
|
|
926 return false;
|
|
927
|
|
928 QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
|
|
929 StringRef NSIntegerName = NSAPIObj->GetNSIntegralKind(qt);
|
|
930
|
|
931 if (NSIntegerName.empty()) {
|
|
932 // Also check for typedef enum {...} TD;
|
|
933 if (const EnumType *EnumTy = qt->getAs<EnumType>()) {
|
|
934 if (EnumTy->getDecl() == EnumDcl) {
|
|
935 bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl);
|
|
936 if (!InsertFoundation(Ctx, TypedefDcl->getBeginLoc()))
|
|
937 return false;
|
|
938 edit::Commit commit(*Editor);
|
|
939 rewriteToNSMacroDecl(Ctx, EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);
|
|
940 Editor->commit(commit);
|
|
941 return true;
|
|
942 }
|
|
943 }
|
|
944 return false;
|
|
945 }
|
|
946
|
|
947 // We may still use NS_OPTIONS based on what we find in the enumertor list.
|
|
948 bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl);
|
|
949 if (!InsertFoundation(Ctx, TypedefDcl->getBeginLoc()))
|
|
950 return false;
|
|
951 edit::Commit commit(*Editor);
|
|
952 bool Res = rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj,
|
|
953 commit, NSIntegerName, NSOptions);
|
|
954 Editor->commit(commit);
|
|
955 return Res;
|
|
956 }
|
|
957
|
|
958 static void ReplaceWithInstancetype(ASTContext &Ctx,
|
|
959 const ObjCMigrateASTConsumer &ASTC,
|
|
960 ObjCMethodDecl *OM) {
|
|
961 if (OM->getReturnType() == Ctx.getObjCInstanceType())
|
|
962 return; // already has instancetype.
|
|
963
|
|
964 SourceRange R;
|
|
965 std::string ClassString;
|
|
966 if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) {
|
|
967 TypeLoc TL = TSInfo->getTypeLoc();
|
|
968 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc());
|
|
969 ClassString = "instancetype";
|
|
970 }
|
|
971 else {
|
|
972 R = SourceRange(OM->getBeginLoc(), OM->getBeginLoc());
|
|
973 ClassString = OM->isInstanceMethod() ? '-' : '+';
|
|
974 ClassString += " (instancetype)";
|
|
975 }
|
|
976 edit::Commit commit(*ASTC.Editor);
|
|
977 commit.replace(R, ClassString);
|
|
978 ASTC.Editor->commit(commit);
|
|
979 }
|
|
980
|
|
981 static void ReplaceWithClasstype(const ObjCMigrateASTConsumer &ASTC,
|
|
982 ObjCMethodDecl *OM) {
|
|
983 ObjCInterfaceDecl *IDecl = OM->getClassInterface();
|
|
984 SourceRange R;
|
|
985 std::string ClassString;
|
|
986 if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) {
|
|
987 TypeLoc TL = TSInfo->getTypeLoc();
|
|
988 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); {
|
|
989 ClassString = std::string(IDecl->getName());
|
|
990 ClassString += "*";
|
|
991 }
|
|
992 }
|
|
993 else {
|
|
994 R = SourceRange(OM->getBeginLoc(), OM->getBeginLoc());
|
|
995 ClassString = "+ (";
|
|
996 ClassString += IDecl->getName(); ClassString += "*)";
|
|
997 }
|
|
998 edit::Commit commit(*ASTC.Editor);
|
|
999 commit.replace(R, ClassString);
|
|
1000 ASTC.Editor->commit(commit);
|
|
1001 }
|
|
1002
|
|
1003 void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx,
|
|
1004 ObjCContainerDecl *CDecl,
|
|
1005 ObjCMethodDecl *OM) {
|
|
1006 ObjCInstanceTypeFamily OIT_Family =
|
|
1007 Selector::getInstTypeMethodFamily(OM->getSelector());
|
|
1008
|
|
1009 std::string ClassName;
|
|
1010 switch (OIT_Family) {
|
|
1011 case OIT_None:
|
|
1012 migrateFactoryMethod(Ctx, CDecl, OM);
|
|
1013 return;
|
|
1014 case OIT_Array:
|
|
1015 ClassName = "NSArray";
|
|
1016 break;
|
|
1017 case OIT_Dictionary:
|
|
1018 ClassName = "NSDictionary";
|
|
1019 break;
|
|
1020 case OIT_Singleton:
|
|
1021 migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton);
|
|
1022 return;
|
|
1023 case OIT_Init:
|
|
1024 if (OM->getReturnType()->isObjCIdType())
|
|
1025 ReplaceWithInstancetype(Ctx, *this, OM);
|
|
1026 return;
|
|
1027 case OIT_ReturnsSelf:
|
|
1028 migrateFactoryMethod(Ctx, CDecl, OM, OIT_ReturnsSelf);
|
|
1029 return;
|
|
1030 }
|
|
1031 if (!OM->getReturnType()->isObjCIdType())
|
|
1032 return;
|
|
1033
|
|
1034 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
|
|
1035 if (!IDecl) {
|
|
1036 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
|
|
1037 IDecl = CatDecl->getClassInterface();
|
|
1038 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
|
|
1039 IDecl = ImpDecl->getClassInterface();
|
|
1040 }
|
|
1041 if (!IDecl ||
|
|
1042 !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) {
|
|
1043 migrateFactoryMethod(Ctx, CDecl, OM);
|
|
1044 return;
|
|
1045 }
|
|
1046 ReplaceWithInstancetype(Ctx, *this, OM);
|
|
1047 }
|
|
1048
|
|
1049 static bool TypeIsInnerPointer(QualType T) {
|
|
1050 if (!T->isAnyPointerType())
|
|
1051 return false;
|
|
1052 if (T->isObjCObjectPointerType() || T->isObjCBuiltinType() ||
|
|
1053 T->isBlockPointerType() || T->isFunctionPointerType() ||
|
|
1054 ento::coreFoundation::isCFObjectRef(T))
|
|
1055 return false;
|
|
1056 // Also, typedef-of-pointer-to-incomplete-struct is something that we assume
|
|
1057 // is not an innter pointer type.
|
|
1058 QualType OrigT = T;
|
|
1059 while (const TypedefType *TD = dyn_cast<TypedefType>(T.getTypePtr()))
|
|
1060 T = TD->getDecl()->getUnderlyingType();
|
|
1061 if (OrigT == T || !T->isPointerType())
|
|
1062 return true;
|
|
1063 const PointerType* PT = T->getAs<PointerType>();
|
|
1064 QualType UPointeeT = PT->getPointeeType().getUnqualifiedType();
|
|
1065 if (UPointeeT->isRecordType()) {
|
|
1066 const RecordType *RecordTy = UPointeeT->getAs<RecordType>();
|
|
1067 if (!RecordTy->getDecl()->isCompleteDefinition())
|
|
1068 return false;
|
|
1069 }
|
|
1070 return true;
|
|
1071 }
|
|
1072
|
|
1073 /// Check whether the two versions match.
|
|
1074 static bool versionsMatch(const VersionTuple &X, const VersionTuple &Y) {
|
|
1075 return (X == Y);
|
|
1076 }
|
|
1077
|
|
1078 /// AvailabilityAttrsMatch - This routine checks that if comparing two
|
|
1079 /// availability attributes, all their components match. It returns
|
|
1080 /// true, if not dealing with availability or when all components of
|
|
1081 /// availability attributes match. This routine is only called when
|
|
1082 /// the attributes are of the same kind.
|
|
1083 static bool AvailabilityAttrsMatch(Attr *At1, Attr *At2) {
|
|
1084 const AvailabilityAttr *AA1 = dyn_cast<AvailabilityAttr>(At1);
|
|
1085 if (!AA1)
|
|
1086 return true;
|
|
1087 const AvailabilityAttr *AA2 = cast<AvailabilityAttr>(At2);
|
|
1088
|
|
1089 VersionTuple Introduced1 = AA1->getIntroduced();
|
|
1090 VersionTuple Deprecated1 = AA1->getDeprecated();
|
|
1091 VersionTuple Obsoleted1 = AA1->getObsoleted();
|
|
1092 bool IsUnavailable1 = AA1->getUnavailable();
|
|
1093 VersionTuple Introduced2 = AA2->getIntroduced();
|
|
1094 VersionTuple Deprecated2 = AA2->getDeprecated();
|
|
1095 VersionTuple Obsoleted2 = AA2->getObsoleted();
|
|
1096 bool IsUnavailable2 = AA2->getUnavailable();
|
|
1097 return (versionsMatch(Introduced1, Introduced2) &&
|
|
1098 versionsMatch(Deprecated1, Deprecated2) &&
|
|
1099 versionsMatch(Obsoleted1, Obsoleted2) &&
|
|
1100 IsUnavailable1 == IsUnavailable2);
|
|
1101 }
|
|
1102
|
|
1103 static bool MatchTwoAttributeLists(const AttrVec &Attrs1, const AttrVec &Attrs2,
|
|
1104 bool &AvailabilityArgsMatch) {
|
|
1105 // This list is very small, so this need not be optimized.
|
|
1106 for (unsigned i = 0, e = Attrs1.size(); i != e; i++) {
|
|
1107 bool match = false;
|
|
1108 for (unsigned j = 0, f = Attrs2.size(); j != f; j++) {
|
|
1109 // Matching attribute kind only. Except for Availability attributes,
|
|
1110 // we are not getting into details of the attributes. For all practical purposes
|
|
1111 // this is sufficient.
|
|
1112 if (Attrs1[i]->getKind() == Attrs2[j]->getKind()) {
|
|
1113 if (AvailabilityArgsMatch)
|
|
1114 AvailabilityArgsMatch = AvailabilityAttrsMatch(Attrs1[i], Attrs2[j]);
|
|
1115 match = true;
|
|
1116 break;
|
|
1117 }
|
|
1118 }
|
|
1119 if (!match)
|
|
1120 return false;
|
|
1121 }
|
|
1122 return true;
|
|
1123 }
|
|
1124
|
|
1125 /// AttributesMatch - This routine checks list of attributes for two
|
|
1126 /// decls. It returns false, if there is a mismatch in kind of
|
|
1127 /// attributes seen in the decls. It returns true if the two decls
|
|
1128 /// have list of same kind of attributes. Furthermore, when there
|
|
1129 /// are availability attributes in the two decls, it sets the
|
|
1130 /// AvailabilityArgsMatch to false if availability attributes have
|
|
1131 /// different versions, etc.
|
|
1132 static bool AttributesMatch(const Decl *Decl1, const Decl *Decl2,
|
|
1133 bool &AvailabilityArgsMatch) {
|
|
1134 if (!Decl1->hasAttrs() || !Decl2->hasAttrs()) {
|
|
1135 AvailabilityArgsMatch = (Decl1->hasAttrs() == Decl2->hasAttrs());
|
|
1136 return true;
|
|
1137 }
|
|
1138 AvailabilityArgsMatch = true;
|
|
1139 const AttrVec &Attrs1 = Decl1->getAttrs();
|
|
1140 const AttrVec &Attrs2 = Decl2->getAttrs();
|
|
1141 bool match = MatchTwoAttributeLists(Attrs1, Attrs2, AvailabilityArgsMatch);
|
|
1142 if (match && (Attrs2.size() > Attrs1.size()))
|
|
1143 return MatchTwoAttributeLists(Attrs2, Attrs1, AvailabilityArgsMatch);
|
|
1144 return match;
|
|
1145 }
|
|
1146
|
|
1147 static bool IsValidIdentifier(ASTContext &Ctx,
|
|
1148 const char *Name) {
|
|
1149 if (!isIdentifierHead(Name[0]))
|
|
1150 return false;
|
|
1151 std::string NameString = Name;
|
|
1152 NameString[0] = toLowercase(NameString[0]);
|
|
1153 IdentifierInfo *II = &Ctx.Idents.get(NameString);
|
|
1154 return II->getTokenID() == tok::identifier;
|
|
1155 }
|
|
1156
|
|
1157 bool ObjCMigrateASTConsumer::migrateProperty(ASTContext &Ctx,
|
|
1158 ObjCContainerDecl *D,
|
|
1159 ObjCMethodDecl *Method) {
|
|
1160 if (Method->isPropertyAccessor() || !Method->isInstanceMethod() ||
|
|
1161 Method->param_size() != 0)
|
|
1162 return false;
|
|
1163 // Is this method candidate to be a getter?
|
|
1164 QualType GRT = Method->getReturnType();
|
|
1165 if (GRT->isVoidType())
|
|
1166 return false;
|
|
1167
|
|
1168 Selector GetterSelector = Method->getSelector();
|
|
1169 ObjCInstanceTypeFamily OIT_Family =
|
|
1170 Selector::getInstTypeMethodFamily(GetterSelector);
|
|
1171
|
|
1172 if (OIT_Family != OIT_None)
|
|
1173 return false;
|
|
1174
|
|
1175 IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);
|
|
1176 Selector SetterSelector =
|
|
1177 SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
|
|
1178 PP.getSelectorTable(),
|
|
1179 getterName);
|
|
1180 ObjCMethodDecl *SetterMethod = D->getInstanceMethod(SetterSelector);
|
|
1181 unsigned LengthOfPrefix = 0;
|
|
1182 if (!SetterMethod) {
|
|
1183 // try a different naming convention for getter: isXxxxx
|
|
1184 StringRef getterNameString = getterName->getName();
|
|
1185 bool IsPrefix = getterNameString.startswith("is");
|
|
1186 // Note that we don't want to change an isXXX method of retainable object
|
|
1187 // type to property (readonly or otherwise).
|
|
1188 if (IsPrefix && GRT->isObjCRetainableType())
|
|
1189 return false;
|
|
1190 if (IsPrefix || getterNameString.startswith("get")) {
|
|
1191 LengthOfPrefix = (IsPrefix ? 2 : 3);
|
|
1192 const char *CGetterName = getterNameString.data() + LengthOfPrefix;
|
|
1193 // Make sure that first character after "is" or "get" prefix can
|
|
1194 // start an identifier.
|
|
1195 if (!IsValidIdentifier(Ctx, CGetterName))
|
|
1196 return false;
|
|
1197 if (CGetterName[0] && isUppercase(CGetterName[0])) {
|
|
1198 getterName = &Ctx.Idents.get(CGetterName);
|
|
1199 SetterSelector =
|
|
1200 SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
|
|
1201 PP.getSelectorTable(),
|
|
1202 getterName);
|
|
1203 SetterMethod = D->getInstanceMethod(SetterSelector);
|
|
1204 }
|
|
1205 }
|
|
1206 }
|
|
1207
|
|
1208 if (SetterMethod) {
|
|
1209 if ((ASTMigrateActions & FrontendOptions::ObjCMT_ReadwriteProperty) == 0)
|
|
1210 return false;
|
|
1211 bool AvailabilityArgsMatch;
|
|
1212 if (SetterMethod->isDeprecated() ||
|
|
1213 !AttributesMatch(Method, SetterMethod, AvailabilityArgsMatch))
|
|
1214 return false;
|
|
1215
|
|
1216 // Is this a valid setter, matching the target getter?
|
|
1217 QualType SRT = SetterMethod->getReturnType();
|
|
1218 if (!SRT->isVoidType())
|
|
1219 return false;
|
|
1220 const ParmVarDecl *argDecl = *SetterMethod->param_begin();
|
|
1221 QualType ArgType = argDecl->getType();
|
|
1222 if (!Ctx.hasSameUnqualifiedType(ArgType, GRT))
|
|
1223 return false;
|
|
1224 edit::Commit commit(*Editor);
|
|
1225 rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit,
|
|
1226 LengthOfPrefix,
|
|
1227 (ASTMigrateActions &
|
|
1228 FrontendOptions::ObjCMT_AtomicProperty) != 0,
|
|
1229 (ASTMigrateActions &
|
|
1230 FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0,
|
|
1231 AvailabilityArgsMatch);
|
|
1232 Editor->commit(commit);
|
|
1233 return true;
|
|
1234 }
|
|
1235 else if (ASTMigrateActions & FrontendOptions::ObjCMT_ReadonlyProperty) {
|
|
1236 // Try a non-void method with no argument (and no setter or property of same name
|
|
1237 // as a 'readonly' property.
|
|
1238 edit::Commit commit(*Editor);
|
|
1239 rewriteToObjCProperty(Method, nullptr /*SetterMethod*/, *NSAPIObj, commit,
|
|
1240 LengthOfPrefix,
|
|
1241 (ASTMigrateActions &
|
|
1242 FrontendOptions::ObjCMT_AtomicProperty) != 0,
|
|
1243 (ASTMigrateActions &
|
|
1244 FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0,
|
|
1245 /*AvailabilityArgsMatch*/false);
|
|
1246 Editor->commit(commit);
|
|
1247 return true;
|
|
1248 }
|
|
1249 return false;
|
|
1250 }
|
|
1251
|
|
1252 void ObjCMigrateASTConsumer::migrateNsReturnsInnerPointer(ASTContext &Ctx,
|
|
1253 ObjCMethodDecl *OM) {
|
|
1254 if (OM->isImplicit() ||
|
|
1255 !OM->isInstanceMethod() ||
|
|
1256 OM->hasAttr<ObjCReturnsInnerPointerAttr>())
|
|
1257 return;
|
|
1258
|
|
1259 QualType RT = OM->getReturnType();
|
|
1260 if (!TypeIsInnerPointer(RT) ||
|
|
1261 !NSAPIObj->isMacroDefined("NS_RETURNS_INNER_POINTER"))
|
|
1262 return;
|
|
1263
|
|
1264 edit::Commit commit(*Editor);
|
|
1265 commit.insertBefore(OM->getEndLoc(), " NS_RETURNS_INNER_POINTER");
|
|
1266 Editor->commit(commit);
|
|
1267 }
|
|
1268
|
|
1269 void ObjCMigrateASTConsumer::migratePropertyNsReturnsInnerPointer(ASTContext &Ctx,
|
|
1270 ObjCPropertyDecl *P) {
|
|
1271 QualType T = P->getType();
|
|
1272
|
|
1273 if (!TypeIsInnerPointer(T) ||
|
|
1274 !NSAPIObj->isMacroDefined("NS_RETURNS_INNER_POINTER"))
|
|
1275 return;
|
|
1276 edit::Commit commit(*Editor);
|
|
1277 commit.insertBefore(P->getEndLoc(), " NS_RETURNS_INNER_POINTER ");
|
|
1278 Editor->commit(commit);
|
|
1279 }
|
|
1280
|
|
1281 void ObjCMigrateASTConsumer::migrateAllMethodInstaceType(ASTContext &Ctx,
|
|
1282 ObjCContainerDecl *CDecl) {
|
|
1283 if (CDecl->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(CDecl))
|
|
1284 return;
|
|
1285
|
|
1286 // migrate methods which can have instancetype as their result type.
|
|
1287 for (auto *Method : CDecl->methods()) {
|
|
1288 if (Method->isDeprecated())
|
|
1289 continue;
|
|
1290 migrateMethodInstanceType(Ctx, CDecl, Method);
|
|
1291 }
|
|
1292 }
|
|
1293
|
|
1294 void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx,
|
|
1295 ObjCContainerDecl *CDecl,
|
|
1296 ObjCMethodDecl *OM,
|
|
1297 ObjCInstanceTypeFamily OIT_Family) {
|
|
1298 if (OM->isInstanceMethod() ||
|
|
1299 OM->getReturnType() == Ctx.getObjCInstanceType() ||
|
|
1300 !OM->getReturnType()->isObjCIdType())
|
|
1301 return;
|
|
1302
|
|
1303 // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class
|
|
1304 // NSYYYNamE with matching names be at least 3 characters long.
|
|
1305 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
|
|
1306 if (!IDecl) {
|
|
1307 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
|
|
1308 IDecl = CatDecl->getClassInterface();
|
|
1309 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
|
|
1310 IDecl = ImpDecl->getClassInterface();
|
|
1311 }
|
|
1312 if (!IDecl)
|
|
1313 return;
|
|
1314
|
|
1315 std::string StringClassName = std::string(IDecl->getName());
|
|
1316 StringRef LoweredClassName(StringClassName);
|
|
1317 std::string StringLoweredClassName = LoweredClassName.lower();
|
|
1318 LoweredClassName = StringLoweredClassName;
|
|
1319
|
|
1320 IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0);
|
|
1321 // Handle method with no name at its first selector slot; e.g. + (id):(int)x.
|
|
1322 if (!MethodIdName)
|
|
1323 return;
|
|
1324
|
|
1325 std::string MethodName = std::string(MethodIdName->getName());
|
|
1326 if (OIT_Family == OIT_Singleton || OIT_Family == OIT_ReturnsSelf) {
|
|
1327 StringRef STRefMethodName(MethodName);
|
|
1328 size_t len = 0;
|
|
1329 if (STRefMethodName.startswith("standard"))
|
|
1330 len = strlen("standard");
|
|
1331 else if (STRefMethodName.startswith("shared"))
|
|
1332 len = strlen("shared");
|
|
1333 else if (STRefMethodName.startswith("default"))
|
|
1334 len = strlen("default");
|
|
1335 else
|
|
1336 return;
|
|
1337 MethodName = std::string(STRefMethodName.substr(len));
|
|
1338 }
|
|
1339 std::string MethodNameSubStr = MethodName.substr(0, 3);
|
|
1340 StringRef MethodNamePrefix(MethodNameSubStr);
|
|
1341 std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower();
|
|
1342 MethodNamePrefix = StringLoweredMethodNamePrefix;
|
|
1343 size_t Ix = LoweredClassName.rfind(MethodNamePrefix);
|
|
1344 if (Ix == StringRef::npos)
|
|
1345 return;
|
|
1346 std::string ClassNamePostfix = std::string(LoweredClassName.substr(Ix));
|
|
1347 StringRef LoweredMethodName(MethodName);
|
|
1348 std::string StringLoweredMethodName = LoweredMethodName.lower();
|
|
1349 LoweredMethodName = StringLoweredMethodName;
|
|
1350 if (!LoweredMethodName.startswith(ClassNamePostfix))
|
|
1351 return;
|
|
1352 if (OIT_Family == OIT_ReturnsSelf)
|
|
1353 ReplaceWithClasstype(*this, OM);
|
|
1354 else
|
|
1355 ReplaceWithInstancetype(Ctx, *this, OM);
|
|
1356 }
|
|
1357
|
|
1358 static bool IsVoidStarType(QualType Ty) {
|
|
1359 if (!Ty->isPointerType())
|
|
1360 return false;
|
|
1361
|
|
1362 while (const TypedefType *TD = dyn_cast<TypedefType>(Ty.getTypePtr()))
|
|
1363 Ty = TD->getDecl()->getUnderlyingType();
|
|
1364
|
|
1365 // Is the type void*?
|
|
1366 const PointerType* PT = Ty->castAs<PointerType>();
|
|
1367 if (PT->getPointeeType().getUnqualifiedType()->isVoidType())
|
|
1368 return true;
|
|
1369 return IsVoidStarType(PT->getPointeeType());
|
|
1370 }
|
|
1371
|
|
1372 /// AuditedType - This routine audits the type AT and returns false if it is one of known
|
|
1373 /// CF object types or of the "void *" variety. It returns true if we don't care about the type
|
|
1374 /// such as a non-pointer or pointers which have no ownership issues (such as "int *").
|
|
1375 static bool AuditedType (QualType AT) {
|
|
1376 if (!AT->isAnyPointerType() && !AT->isBlockPointerType())
|
|
1377 return true;
|
|
1378 // FIXME. There isn't much we can say about CF pointer type; or is there?
|
|
1379 if (ento::coreFoundation::isCFObjectRef(AT) ||
|
|
1380 IsVoidStarType(AT) ||
|
|
1381 // If an ObjC object is type, assuming that it is not a CF function and
|
|
1382 // that it is an un-audited function.
|
|
1383 AT->isObjCObjectPointerType() || AT->isObjCBuiltinType())
|
|
1384 return false;
|
|
1385 // All other pointers are assumed audited as harmless.
|
|
1386 return true;
|
|
1387 }
|
|
1388
|
|
1389 void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) {
|
|
1390 if (CFFunctionIBCandidates.empty())
|
|
1391 return;
|
|
1392 if (!NSAPIObj->isMacroDefined("CF_IMPLICIT_BRIDGING_ENABLED")) {
|
|
1393 CFFunctionIBCandidates.clear();
|
|
1394 FileId = FileID();
|
|
1395 return;
|
|
1396 }
|
|
1397 // Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED
|
|
1398 const Decl *FirstFD = CFFunctionIBCandidates[0];
|
|
1399 const Decl *LastFD =
|
|
1400 CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1];
|
|
1401 const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n";
|
|
1402 edit::Commit commit(*Editor);
|
|
1403 commit.insertBefore(FirstFD->getBeginLoc(), PragmaString);
|
|
1404 PragmaString = "\n\nCF_IMPLICIT_BRIDGING_DISABLED\n";
|
|
1405 SourceLocation EndLoc = LastFD->getEndLoc();
|
|
1406 // get location just past end of function location.
|
|
1407 EndLoc = PP.getLocForEndOfToken(EndLoc);
|
|
1408 if (isa<FunctionDecl>(LastFD)) {
|
|
1409 // For Methods, EndLoc points to the ending semcolon. So,
|
|
1410 // not of these extra work is needed.
|
|
1411 Token Tok;
|
|
1412 // get locaiton of token that comes after end of function.
|
|
1413 bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true);
|
|
1414 if (!Failed)
|
|
1415 EndLoc = Tok.getLocation();
|
|
1416 }
|
|
1417 commit.insertAfterToken(EndLoc, PragmaString);
|
|
1418 Editor->commit(commit);
|
|
1419 FileId = FileID();
|
|
1420 CFFunctionIBCandidates.clear();
|
|
1421 }
|
|
1422
|
|
1423 void ObjCMigrateASTConsumer::migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl) {
|
|
1424 if (Decl->isDeprecated())
|
|
1425 return;
|
|
1426
|
|
1427 if (Decl->hasAttr<CFAuditedTransferAttr>()) {
|
|
1428 assert(CFFunctionIBCandidates.empty() &&
|
|
1429 "Cannot have audited functions/methods inside user "
|
|
1430 "provided CF_IMPLICIT_BRIDGING_ENABLE");
|
|
1431 return;
|
|
1432 }
|
|
1433
|
|
1434 // Finction must be annotated first.
|
|
1435 if (const FunctionDecl *FuncDecl = dyn_cast<FunctionDecl>(Decl)) {
|
|
1436 CF_BRIDGING_KIND AuditKind = migrateAddFunctionAnnotation(Ctx, FuncDecl);
|
|
1437 if (AuditKind == CF_BRIDGING_ENABLE) {
|
|
1438 CFFunctionIBCandidates.push_back(Decl);
|
|
1439 if (FileId.isInvalid())
|
|
1440 FileId = PP.getSourceManager().getFileID(Decl->getLocation());
|
|
1441 }
|
|
1442 else if (AuditKind == CF_BRIDGING_MAY_INCLUDE) {
|
|
1443 if (!CFFunctionIBCandidates.empty()) {
|
|
1444 CFFunctionIBCandidates.push_back(Decl);
|
|
1445 if (FileId.isInvalid())
|
|
1446 FileId = PP.getSourceManager().getFileID(Decl->getLocation());
|
|
1447 }
|
|
1448 }
|
|
1449 else
|
|
1450 AnnotateImplicitBridging(Ctx);
|
|
1451 }
|
|
1452 else {
|
|
1453 migrateAddMethodAnnotation(Ctx, cast<ObjCMethodDecl>(Decl));
|
|
1454 AnnotateImplicitBridging(Ctx);
|
|
1455 }
|
|
1456 }
|
|
1457
|
|
1458 void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx,
|
|
1459 const RetainSummary *RS,
|
|
1460 const FunctionDecl *FuncDecl,
|
|
1461 bool ResultAnnotated) {
|
|
1462 // Annotate function.
|
|
1463 if (!ResultAnnotated) {
|
|
1464 RetEffect Ret = RS->getRetEffect();
|
|
1465 const char *AnnotationString = nullptr;
|
|
1466 if (Ret.getObjKind() == ObjKind::CF) {
|
|
1467 if (Ret.isOwned() && NSAPIObj->isMacroDefined("CF_RETURNS_RETAINED"))
|
|
1468 AnnotationString = " CF_RETURNS_RETAINED";
|
|
1469 else if (Ret.notOwned() &&
|
|
1470 NSAPIObj->isMacroDefined("CF_RETURNS_NOT_RETAINED"))
|
|
1471 AnnotationString = " CF_RETURNS_NOT_RETAINED";
|
|
1472 }
|
|
1473 else if (Ret.getObjKind() == ObjKind::ObjC) {
|
|
1474 if (Ret.isOwned() && NSAPIObj->isMacroDefined("NS_RETURNS_RETAINED"))
|
|
1475 AnnotationString = " NS_RETURNS_RETAINED";
|
|
1476 }
|
|
1477
|
|
1478 if (AnnotationString) {
|
|
1479 edit::Commit commit(*Editor);
|
|
1480 commit.insertAfterToken(FuncDecl->getEndLoc(), AnnotationString);
|
|
1481 Editor->commit(commit);
|
|
1482 }
|
|
1483 }
|
|
1484 unsigned i = 0;
|
|
1485 for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),
|
|
1486 pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {
|
|
1487 const ParmVarDecl *pd = *pi;
|
|
1488 ArgEffect AE = RS->getArg(i);
|
|
1489 if (AE.getKind() == DecRef && AE.getObjKind() == ObjKind::CF &&
|
|
1490 !pd->hasAttr<CFConsumedAttr>() &&
|
|
1491 NSAPIObj->isMacroDefined("CF_CONSUMED")) {
|
|
1492 edit::Commit commit(*Editor);
|
|
1493 commit.insertBefore(pd->getLocation(), "CF_CONSUMED ");
|
|
1494 Editor->commit(commit);
|
|
1495 } else if (AE.getKind() == DecRef && AE.getObjKind() == ObjKind::ObjC &&
|
|
1496 !pd->hasAttr<NSConsumedAttr>() &&
|
|
1497 NSAPIObj->isMacroDefined("NS_CONSUMED")) {
|
|
1498 edit::Commit commit(*Editor);
|
|
1499 commit.insertBefore(pd->getLocation(), "NS_CONSUMED ");
|
|
1500 Editor->commit(commit);
|
|
1501 }
|
|
1502 }
|
|
1503 }
|
|
1504
|
|
1505 ObjCMigrateASTConsumer::CF_BRIDGING_KIND
|
|
1506 ObjCMigrateASTConsumer::migrateAddFunctionAnnotation(
|
|
1507 ASTContext &Ctx,
|
|
1508 const FunctionDecl *FuncDecl) {
|
|
1509 if (FuncDecl->hasBody())
|
|
1510 return CF_BRIDGING_NONE;
|
|
1511
|
|
1512 const RetainSummary *RS =
|
|
1513 getSummaryManager(Ctx).getSummary(AnyCall(FuncDecl));
|
|
1514 bool FuncIsReturnAnnotated = (FuncDecl->hasAttr<CFReturnsRetainedAttr>() ||
|
|
1515 FuncDecl->hasAttr<CFReturnsNotRetainedAttr>() ||
|
|
1516 FuncDecl->hasAttr<NSReturnsRetainedAttr>() ||
|
|
1517 FuncDecl->hasAttr<NSReturnsNotRetainedAttr>() ||
|
|
1518 FuncDecl->hasAttr<NSReturnsAutoreleasedAttr>());
|
|
1519
|
|
1520 // Trivial case of when function is annotated and has no argument.
|
|
1521 if (FuncIsReturnAnnotated && FuncDecl->getNumParams() == 0)
|
|
1522 return CF_BRIDGING_NONE;
|
|
1523
|
|
1524 bool ReturnCFAudited = false;
|
|
1525 if (!FuncIsReturnAnnotated) {
|
|
1526 RetEffect Ret = RS->getRetEffect();
|
|
1527 if (Ret.getObjKind() == ObjKind::CF &&
|
|
1528 (Ret.isOwned() || Ret.notOwned()))
|
|
1529 ReturnCFAudited = true;
|
|
1530 else if (!AuditedType(FuncDecl->getReturnType()))
|
|
1531 return CF_BRIDGING_NONE;
|
|
1532 }
|
|
1533
|
|
1534 // At this point result type is audited for potential inclusion.
|
|
1535 unsigned i = 0;
|
|
1536 bool ArgCFAudited = false;
|
|
1537 for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),
|
|
1538 pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {
|
|
1539 const ParmVarDecl *pd = *pi;
|
|
1540 ArgEffect AE = RS->getArg(i);
|
|
1541 if ((AE.getKind() == DecRef /*CFConsumed annotated*/ ||
|
|
1542 AE.getKind() == IncRef) && AE.getObjKind() == ObjKind::CF) {
|
|
1543 if (AE.getKind() == DecRef && !pd->hasAttr<CFConsumedAttr>())
|
|
1544 ArgCFAudited = true;
|
|
1545 else if (AE.getKind() == IncRef)
|
|
1546 ArgCFAudited = true;
|
|
1547 } else {
|
|
1548 QualType AT = pd->getType();
|
|
1549 if (!AuditedType(AT)) {
|
|
1550 AddCFAnnotations(Ctx, RS, FuncDecl, FuncIsReturnAnnotated);
|
|
1551 return CF_BRIDGING_NONE;
|
|
1552 }
|
|
1553 }
|
|
1554 }
|
|
1555 if (ReturnCFAudited || ArgCFAudited)
|
|
1556 return CF_BRIDGING_ENABLE;
|
|
1557
|
|
1558 return CF_BRIDGING_MAY_INCLUDE;
|
|
1559 }
|
|
1560
|
|
1561 void ObjCMigrateASTConsumer::migrateARCSafeAnnotation(ASTContext &Ctx,
|
|
1562 ObjCContainerDecl *CDecl) {
|
|
1563 if (!isa<ObjCInterfaceDecl>(CDecl) || CDecl->isDeprecated())
|
|
1564 return;
|
|
1565
|
|
1566 // migrate methods which can have instancetype as their result type.
|
|
1567 for (const auto *Method : CDecl->methods())
|
|
1568 migrateCFAnnotation(Ctx, Method);
|
|
1569 }
|
|
1570
|
|
1571 void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx,
|
|
1572 const RetainSummary *RS,
|
|
1573 const ObjCMethodDecl *MethodDecl,
|
|
1574 bool ResultAnnotated) {
|
|
1575 // Annotate function.
|
|
1576 if (!ResultAnnotated) {
|
|
1577 RetEffect Ret = RS->getRetEffect();
|
|
1578 const char *AnnotationString = nullptr;
|
|
1579 if (Ret.getObjKind() == ObjKind::CF) {
|
|
1580 if (Ret.isOwned() && NSAPIObj->isMacroDefined("CF_RETURNS_RETAINED"))
|
|
1581 AnnotationString = " CF_RETURNS_RETAINED";
|
|
1582 else if (Ret.notOwned() &&
|
|
1583 NSAPIObj->isMacroDefined("CF_RETURNS_NOT_RETAINED"))
|
|
1584 AnnotationString = " CF_RETURNS_NOT_RETAINED";
|
|
1585 }
|
|
1586 else if (Ret.getObjKind() == ObjKind::ObjC) {
|
|
1587 ObjCMethodFamily OMF = MethodDecl->getMethodFamily();
|
|
1588 switch (OMF) {
|
|
1589 case clang::OMF_alloc:
|
|
1590 case clang::OMF_new:
|
|
1591 case clang::OMF_copy:
|
|
1592 case clang::OMF_init:
|
|
1593 case clang::OMF_mutableCopy:
|
|
1594 break;
|
|
1595
|
|
1596 default:
|
|
1597 if (Ret.isOwned() && NSAPIObj->isMacroDefined("NS_RETURNS_RETAINED"))
|
|
1598 AnnotationString = " NS_RETURNS_RETAINED";
|
|
1599 break;
|
|
1600 }
|
|
1601 }
|
|
1602
|
|
1603 if (AnnotationString) {
|
|
1604 edit::Commit commit(*Editor);
|
|
1605 commit.insertBefore(MethodDecl->getEndLoc(), AnnotationString);
|
|
1606 Editor->commit(commit);
|
|
1607 }
|
|
1608 }
|
|
1609 unsigned i = 0;
|
|
1610 for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(),
|
|
1611 pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) {
|
|
1612 const ParmVarDecl *pd = *pi;
|
|
1613 ArgEffect AE = RS->getArg(i);
|
|
1614 if (AE.getKind() == DecRef
|
|
1615 && AE.getObjKind() == ObjKind::CF
|
|
1616 && !pd->hasAttr<CFConsumedAttr>() &&
|
|
1617 NSAPIObj->isMacroDefined("CF_CONSUMED")) {
|
|
1618 edit::Commit commit(*Editor);
|
|
1619 commit.insertBefore(pd->getLocation(), "CF_CONSUMED ");
|
|
1620 Editor->commit(commit);
|
|
1621 }
|
|
1622 }
|
|
1623 }
|
|
1624
|
|
1625 void ObjCMigrateASTConsumer::migrateAddMethodAnnotation(
|
|
1626 ASTContext &Ctx,
|
|
1627 const ObjCMethodDecl *MethodDecl) {
|
|
1628 if (MethodDecl->hasBody() || MethodDecl->isImplicit())
|
|
1629 return;
|
|
1630
|
|
1631 const RetainSummary *RS =
|
|
1632 getSummaryManager(Ctx).getSummary(AnyCall(MethodDecl));
|
|
1633
|
|
1634 bool MethodIsReturnAnnotated =
|
|
1635 (MethodDecl->hasAttr<CFReturnsRetainedAttr>() ||
|
|
1636 MethodDecl->hasAttr<CFReturnsNotRetainedAttr>() ||
|
|
1637 MethodDecl->hasAttr<NSReturnsRetainedAttr>() ||
|
|
1638 MethodDecl->hasAttr<NSReturnsNotRetainedAttr>() ||
|
|
1639 MethodDecl->hasAttr<NSReturnsAutoreleasedAttr>());
|
|
1640
|
|
1641 if (RS->getReceiverEffect().getKind() == DecRef &&
|
|
1642 !MethodDecl->hasAttr<NSConsumesSelfAttr>() &&
|
|
1643 MethodDecl->getMethodFamily() != OMF_init &&
|
|
1644 MethodDecl->getMethodFamily() != OMF_release &&
|
|
1645 NSAPIObj->isMacroDefined("NS_CONSUMES_SELF")) {
|
|
1646 edit::Commit commit(*Editor);
|
|
1647 commit.insertBefore(MethodDecl->getEndLoc(), " NS_CONSUMES_SELF");
|
|
1648 Editor->commit(commit);
|
|
1649 }
|
|
1650
|
|
1651 // Trivial case of when function is annotated and has no argument.
|
|
1652 if (MethodIsReturnAnnotated &&
|
|
1653 (MethodDecl->param_begin() == MethodDecl->param_end()))
|
|
1654 return;
|
|
1655
|
|
1656 if (!MethodIsReturnAnnotated) {
|
|
1657 RetEffect Ret = RS->getRetEffect();
|
|
1658 if ((Ret.getObjKind() == ObjKind::CF ||
|
|
1659 Ret.getObjKind() == ObjKind::ObjC) &&
|
|
1660 (Ret.isOwned() || Ret.notOwned())) {
|
|
1661 AddCFAnnotations(Ctx, RS, MethodDecl, false);
|
|
1662 return;
|
|
1663 } else if (!AuditedType(MethodDecl->getReturnType()))
|
|
1664 return;
|
|
1665 }
|
|
1666
|
|
1667 // At this point result type is either annotated or audited.
|
|
1668 unsigned i = 0;
|
|
1669 for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(),
|
|
1670 pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) {
|
|
1671 const ParmVarDecl *pd = *pi;
|
|
1672 ArgEffect AE = RS->getArg(i);
|
|
1673 if ((AE.getKind() == DecRef && !pd->hasAttr<CFConsumedAttr>()) ||
|
|
1674 AE.getKind() == IncRef || !AuditedType(pd->getType())) {
|
|
1675 AddCFAnnotations(Ctx, RS, MethodDecl, MethodIsReturnAnnotated);
|
|
1676 return;
|
|
1677 }
|
|
1678 }
|
|
1679 }
|
|
1680
|
|
1681 namespace {
|
|
1682 class SuperInitChecker : public RecursiveASTVisitor<SuperInitChecker> {
|
|
1683 public:
|
|
1684 bool shouldVisitTemplateInstantiations() const { return false; }
|
|
1685 bool shouldWalkTypesOfTypeLocs() const { return false; }
|
|
1686
|
|
1687 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
|
|
1688 if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) {
|
|
1689 if (E->getMethodFamily() == OMF_init)
|
|
1690 return false;
|
|
1691 }
|
|
1692 return true;
|
|
1693 }
|
|
1694 };
|
|
1695 } // end anonymous namespace
|
|
1696
|
|
1697 static bool hasSuperInitCall(const ObjCMethodDecl *MD) {
|
|
1698 return !SuperInitChecker().TraverseStmt(MD->getBody());
|
|
1699 }
|
|
1700
|
|
1701 void ObjCMigrateASTConsumer::inferDesignatedInitializers(
|
|
1702 ASTContext &Ctx,
|
|
1703 const ObjCImplementationDecl *ImplD) {
|
|
1704
|
|
1705 const ObjCInterfaceDecl *IFace = ImplD->getClassInterface();
|
|
1706 if (!IFace || IFace->hasDesignatedInitializers())
|
|
1707 return;
|
|
1708 if (!NSAPIObj->isMacroDefined("NS_DESIGNATED_INITIALIZER"))
|
|
1709 return;
|
|
1710
|
|
1711 for (const auto *MD : ImplD->instance_methods()) {
|
|
1712 if (MD->isDeprecated() ||
|
|
1713 MD->getMethodFamily() != OMF_init ||
|
|
1714 MD->isDesignatedInitializerForTheInterface())
|
|
1715 continue;
|
|
1716 const ObjCMethodDecl *IFaceM = IFace->getMethod(MD->getSelector(),
|
|
1717 /*isInstance=*/true);
|
|
1718 if (!IFaceM)
|
|
1719 continue;
|
|
1720 if (hasSuperInitCall(MD)) {
|
|
1721 edit::Commit commit(*Editor);
|
|
1722 commit.insert(IFaceM->getEndLoc(), " NS_DESIGNATED_INITIALIZER");
|
|
1723 Editor->commit(commit);
|
|
1724 }
|
|
1725 }
|
|
1726 }
|
|
1727
|
|
1728 bool ObjCMigrateASTConsumer::InsertFoundation(ASTContext &Ctx,
|
|
1729 SourceLocation Loc) {
|
|
1730 if (FoundationIncluded)
|
|
1731 return true;
|
|
1732 if (Loc.isInvalid())
|
|
1733 return false;
|
|
1734 auto *nsEnumId = &Ctx.Idents.get("NS_ENUM");
|
|
1735 if (PP.getMacroDefinitionAtLoc(nsEnumId, Loc)) {
|
|
1736 FoundationIncluded = true;
|
|
1737 return true;
|
|
1738 }
|
|
1739 edit::Commit commit(*Editor);
|
|
1740 if (Ctx.getLangOpts().Modules)
|
|
1741 commit.insert(Loc, "#ifndef NS_ENUM\n@import Foundation;\n#endif\n");
|
|
1742 else
|
|
1743 commit.insert(Loc, "#ifndef NS_ENUM\n#import <Foundation/Foundation.h>\n#endif\n");
|
|
1744 Editor->commit(commit);
|
|
1745 FoundationIncluded = true;
|
|
1746 return true;
|
|
1747 }
|
|
1748
|
|
1749 namespace {
|
|
1750
|
|
1751 class RewritesReceiver : public edit::EditsReceiver {
|
|
1752 Rewriter &Rewrite;
|
|
1753
|
|
1754 public:
|
|
1755 RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
|
|
1756
|
|
1757 void insert(SourceLocation loc, StringRef text) override {
|
|
1758 Rewrite.InsertText(loc, text);
|
|
1759 }
|
|
1760 void replace(CharSourceRange range, StringRef text) override {
|
|
1761 Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
|
|
1762 }
|
|
1763 };
|
|
1764
|
|
1765 class JSONEditWriter : public edit::EditsReceiver {
|
|
1766 SourceManager &SourceMgr;
|
|
1767 llvm::raw_ostream &OS;
|
|
1768
|
|
1769 public:
|
|
1770 JSONEditWriter(SourceManager &SM, llvm::raw_ostream &OS)
|
|
1771 : SourceMgr(SM), OS(OS) {
|
|
1772 OS << "[\n";
|
|
1773 }
|
|
1774 ~JSONEditWriter() override { OS << "]\n"; }
|
|
1775
|
|
1776 private:
|
|
1777 struct EntryWriter {
|
|
1778 SourceManager &SourceMgr;
|
|
1779 llvm::raw_ostream &OS;
|
|
1780
|
|
1781 EntryWriter(SourceManager &SM, llvm::raw_ostream &OS)
|
|
1782 : SourceMgr(SM), OS(OS) {
|
|
1783 OS << " {\n";
|
|
1784 }
|
|
1785 ~EntryWriter() {
|
|
1786 OS << " },\n";
|
|
1787 }
|
|
1788
|
|
1789 void writeLoc(SourceLocation Loc) {
|
|
1790 FileID FID;
|
|
1791 unsigned Offset;
|
|
1792 std::tie(FID, Offset) = SourceMgr.getDecomposedLoc(Loc);
|
|
1793 assert(FID.isValid());
|
|
1794 SmallString<200> Path =
|
|
1795 StringRef(SourceMgr.getFileEntryForID(FID)->getName());
|
|
1796 llvm::sys::fs::make_absolute(Path);
|
|
1797 OS << " \"file\": \"";
|
|
1798 OS.write_escaped(Path.str()) << "\",\n";
|
|
1799 OS << " \"offset\": " << Offset << ",\n";
|
|
1800 }
|
|
1801
|
|
1802 void writeRemove(CharSourceRange Range) {
|
|
1803 assert(Range.isCharRange());
|
|
1804 std::pair<FileID, unsigned> Begin =
|
|
1805 SourceMgr.getDecomposedLoc(Range.getBegin());
|
|
1806 std::pair<FileID, unsigned> End =
|
|
1807 SourceMgr.getDecomposedLoc(Range.getEnd());
|
|
1808 assert(Begin.first == End.first);
|
|
1809 assert(Begin.second <= End.second);
|
|
1810 unsigned Length = End.second - Begin.second;
|
|
1811
|
|
1812 OS << " \"remove\": " << Length << ",\n";
|
|
1813 }
|
|
1814
|
|
1815 void writeText(StringRef Text) {
|
|
1816 OS << " \"text\": \"";
|
|
1817 OS.write_escaped(Text) << "\",\n";
|
|
1818 }
|
|
1819 };
|
|
1820
|
|
1821 void insert(SourceLocation Loc, StringRef Text) override {
|
|
1822 EntryWriter Writer(SourceMgr, OS);
|
|
1823 Writer.writeLoc(Loc);
|
|
1824 Writer.writeText(Text);
|
|
1825 }
|
|
1826
|
|
1827 void replace(CharSourceRange Range, StringRef Text) override {
|
|
1828 EntryWriter Writer(SourceMgr, OS);
|
|
1829 Writer.writeLoc(Range.getBegin());
|
|
1830 Writer.writeRemove(Range);
|
|
1831 Writer.writeText(Text);
|
|
1832 }
|
|
1833
|
|
1834 void remove(CharSourceRange Range) override {
|
|
1835 EntryWriter Writer(SourceMgr, OS);
|
|
1836 Writer.writeLoc(Range.getBegin());
|
|
1837 Writer.writeRemove(Range);
|
|
1838 }
|
|
1839 };
|
|
1840
|
|
1841 } // end anonymous namespace
|
|
1842
|
|
1843 void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
|
|
1844
|
|
1845 TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
|
|
1846 if (ASTMigrateActions & FrontendOptions::ObjCMT_MigrateDecls) {
|
|
1847 for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();
|
|
1848 D != DEnd; ++D) {
|
|
1849 FileID FID = PP.getSourceManager().getFileID((*D)->getLocation());
|
|
1850 if (FID.isValid())
|
|
1851 if (FileId.isValid() && FileId != FID) {
|
|
1852 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
|
|
1853 AnnotateImplicitBridging(Ctx);
|
|
1854 }
|
|
1855
|
|
1856 if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))
|
|
1857 if (canModify(CDecl))
|
|
1858 migrateObjCContainerDecl(Ctx, CDecl);
|
|
1859 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(*D)) {
|
|
1860 if (canModify(CatDecl))
|
|
1861 migrateObjCContainerDecl(Ctx, CatDecl);
|
|
1862 }
|
|
1863 else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D)) {
|
|
1864 ObjCProtocolDecls.insert(PDecl->getCanonicalDecl());
|
|
1865 if (canModify(PDecl))
|
|
1866 migrateObjCContainerDecl(Ctx, PDecl);
|
|
1867 }
|
|
1868 else if (const ObjCImplementationDecl *ImpDecl =
|
|
1869 dyn_cast<ObjCImplementationDecl>(*D)) {
|
|
1870 if ((ASTMigrateActions & FrontendOptions::ObjCMT_ProtocolConformance) &&
|
|
1871 canModify(ImpDecl))
|
|
1872 migrateProtocolConformance(Ctx, ImpDecl);
|
|
1873 }
|
|
1874 else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {
|
|
1875 if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros))
|
|
1876 continue;
|
|
1877 if (!canModify(ED))
|
|
1878 continue;
|
|
1879 DeclContext::decl_iterator N = D;
|
|
1880 if (++N != DEnd) {
|
|
1881 const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N);
|
|
1882 if (migrateNSEnumDecl(Ctx, ED, TD) && TD)
|
|
1883 D++;
|
|
1884 }
|
|
1885 else
|
|
1886 migrateNSEnumDecl(Ctx, ED, /*TypedefDecl */nullptr);
|
|
1887 }
|
|
1888 else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*D)) {
|
|
1889 if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros))
|
|
1890 continue;
|
|
1891 if (!canModify(TD))
|
|
1892 continue;
|
|
1893 DeclContext::decl_iterator N = D;
|
|
1894 if (++N == DEnd)
|
|
1895 continue;
|
|
1896 if (const EnumDecl *ED = dyn_cast<EnumDecl>(*N)) {
|
|
1897 if (canModify(ED)) {
|
|
1898 if (++N != DEnd)
|
|
1899 if (const TypedefDecl *TDF = dyn_cast<TypedefDecl>(*N)) {
|
|
1900 // prefer typedef-follows-enum to enum-follows-typedef pattern.
|
|
1901 if (migrateNSEnumDecl(Ctx, ED, TDF)) {
|
|
1902 ++D; ++D;
|
|
1903 CacheObjCNSIntegerTypedefed(TD);
|
|
1904 continue;
|
|
1905 }
|
|
1906 }
|
|
1907 if (migrateNSEnumDecl(Ctx, ED, TD)) {
|
|
1908 ++D;
|
|
1909 continue;
|
|
1910 }
|
|
1911 }
|
|
1912 }
|
|
1913 CacheObjCNSIntegerTypedefed(TD);
|
|
1914 }
|
|
1915 else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D)) {
|
|
1916 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
|
|
1917 canModify(FD))
|
|
1918 migrateCFAnnotation(Ctx, FD);
|
|
1919 }
|
|
1920
|
|
1921 if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) {
|
|
1922 bool CanModify = canModify(CDecl);
|
|
1923 // migrate methods which can have instancetype as their result type.
|
|
1924 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Instancetype) &&
|
|
1925 CanModify)
|
|
1926 migrateAllMethodInstaceType(Ctx, CDecl);
|
|
1927 // annotate methods with CF annotations.
|
|
1928 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
|
|
1929 CanModify)
|
|
1930 migrateARCSafeAnnotation(Ctx, CDecl);
|
|
1931 }
|
|
1932
|
|
1933 if (const ObjCImplementationDecl *
|
|
1934 ImplD = dyn_cast<ObjCImplementationDecl>(*D)) {
|
|
1935 if ((ASTMigrateActions & FrontendOptions::ObjCMT_DesignatedInitializer) &&
|
|
1936 canModify(ImplD))
|
|
1937 inferDesignatedInitializers(Ctx, ImplD);
|
|
1938 }
|
|
1939 }
|
|
1940 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
|
|
1941 AnnotateImplicitBridging(Ctx);
|
|
1942 }
|
|
1943
|
|
1944 if (IsOutputFile) {
|
|
1945 std::error_code EC;
|
|
1946 llvm::raw_fd_ostream OS(MigrateDir, EC, llvm::sys::fs::OF_None);
|
|
1947 if (EC) {
|
|
1948 DiagnosticsEngine &Diags = Ctx.getDiagnostics();
|
|
1949 Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
|
|
1950 << EC.message();
|
|
1951 return;
|
|
1952 }
|
|
1953
|
|
1954 JSONEditWriter Writer(Ctx.getSourceManager(), OS);
|
|
1955 Editor->applyRewrites(Writer);
|
|
1956 return;
|
|
1957 }
|
|
1958
|
|
1959 Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
|
|
1960 RewritesReceiver Rec(rewriter);
|
|
1961 Editor->applyRewrites(Rec);
|
|
1962
|
|
1963 for (Rewriter::buffer_iterator
|
|
1964 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
|
|
1965 FileID FID = I->first;
|
|
1966 RewriteBuffer &buf = I->second;
|
|
1967 const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
|
|
1968 assert(file);
|
|
1969 SmallString<512> newText;
|
|
1970 llvm::raw_svector_ostream vecOS(newText);
|
|
1971 buf.write(vecOS);
|
|
1972 std::unique_ptr<llvm::MemoryBuffer> memBuf(
|
|
1973 llvm::MemoryBuffer::getMemBufferCopy(
|
|
1974 StringRef(newText.data(), newText.size()), file->getName()));
|
|
1975 SmallString<64> filePath(file->getName());
|
|
1976 FileMgr.FixupRelativePath(filePath);
|
|
1977 Remapper.remap(filePath.str(), std::move(memBuf));
|
|
1978 }
|
|
1979
|
|
1980 if (IsOutputFile) {
|
|
1981 Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
|
|
1982 } else {
|
|
1983 Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
|
|
1984 }
|
|
1985 }
|
|
1986
|
|
1987 bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
|
|
1988 CI.getDiagnostics().setIgnoreAllWarnings(true);
|
|
1989 return true;
|
|
1990 }
|
|
1991
|
|
1992 static std::vector<std::string> getWhiteListFilenames(StringRef DirPath) {
|
|
1993 using namespace llvm::sys::fs;
|
|
1994 using namespace llvm::sys::path;
|
|
1995
|
|
1996 std::vector<std::string> Filenames;
|
|
1997 if (DirPath.empty() || !is_directory(DirPath))
|
|
1998 return Filenames;
|
|
1999
|
|
2000 std::error_code EC;
|
|
2001 directory_iterator DI = directory_iterator(DirPath, EC);
|
|
2002 directory_iterator DE;
|
|
2003 for (; !EC && DI != DE; DI = DI.increment(EC)) {
|
|
2004 if (is_regular_file(DI->path()))
|
|
2005 Filenames.push_back(std::string(filename(DI->path())));
|
|
2006 }
|
|
2007
|
|
2008 return Filenames;
|
|
2009 }
|
|
2010
|
|
2011 std::unique_ptr<ASTConsumer>
|
|
2012 MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
|
|
2013 PPConditionalDirectiveRecord *
|
|
2014 PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
|
|
2015 unsigned ObjCMTAction = CI.getFrontendOpts().ObjCMTAction;
|
|
2016 unsigned ObjCMTOpts = ObjCMTAction;
|
|
2017 // These are companion flags, they do not enable transformations.
|
|
2018 ObjCMTOpts &= ~(FrontendOptions::ObjCMT_AtomicProperty |
|
|
2019 FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty);
|
|
2020 if (ObjCMTOpts == FrontendOptions::ObjCMT_None) {
|
|
2021 // If no specific option was given, enable literals+subscripting transforms
|
|
2022 // by default.
|
|
2023 ObjCMTAction |= FrontendOptions::ObjCMT_Literals |
|
|
2024 FrontendOptions::ObjCMT_Subscripting;
|
|
2025 }
|
|
2026 CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec));
|
|
2027 std::vector<std::string> WhiteList =
|
|
2028 getWhiteListFilenames(CI.getFrontendOpts().ObjCMTWhiteListPath);
|
|
2029 return std::make_unique<ObjCMigrateASTConsumer>(
|
|
2030 CI.getFrontendOpts().OutputFile, ObjCMTAction, Remapper,
|
|
2031 CI.getFileManager(), PPRec, CI.getPreprocessor(),
|
|
2032 /*isOutputFile=*/true, WhiteList);
|
|
2033 }
|
|
2034
|
|
2035 namespace {
|
|
2036 struct EditEntry {
|
|
2037 const FileEntry *File;
|
|
2038 unsigned Offset;
|
|
2039 unsigned RemoveLen;
|
|
2040 std::string Text;
|
|
2041
|
|
2042 EditEntry() : File(), Offset(), RemoveLen() {}
|
|
2043 };
|
|
2044 } // end anonymous namespace
|
|
2045
|
|
2046 namespace llvm {
|
|
2047 template<> struct DenseMapInfo<EditEntry> {
|
|
2048 static inline EditEntry getEmptyKey() {
|
|
2049 EditEntry Entry;
|
|
2050 Entry.Offset = unsigned(-1);
|
|
2051 return Entry;
|
|
2052 }
|
|
2053 static inline EditEntry getTombstoneKey() {
|
|
2054 EditEntry Entry;
|
|
2055 Entry.Offset = unsigned(-2);
|
|
2056 return Entry;
|
|
2057 }
|
|
2058 static unsigned getHashValue(const EditEntry& Val) {
|
|
2059 llvm::FoldingSetNodeID ID;
|
|
2060 ID.AddPointer(Val.File);
|
|
2061 ID.AddInteger(Val.Offset);
|
|
2062 ID.AddInteger(Val.RemoveLen);
|
|
2063 ID.AddString(Val.Text);
|
|
2064 return ID.ComputeHash();
|
|
2065 }
|
|
2066 static bool isEqual(const EditEntry &LHS, const EditEntry &RHS) {
|
|
2067 return LHS.File == RHS.File &&
|
|
2068 LHS.Offset == RHS.Offset &&
|
|
2069 LHS.RemoveLen == RHS.RemoveLen &&
|
|
2070 LHS.Text == RHS.Text;
|
|
2071 }
|
|
2072 };
|
|
2073 } // end namespace llvm
|
|
2074
|
|
2075 namespace {
|
|
2076 class RemapFileParser {
|
|
2077 FileManager &FileMgr;
|
|
2078
|
|
2079 public:
|
|
2080 RemapFileParser(FileManager &FileMgr) : FileMgr(FileMgr) { }
|
|
2081
|
|
2082 bool parse(StringRef File, SmallVectorImpl<EditEntry> &Entries) {
|
|
2083 using namespace llvm::yaml;
|
|
2084
|
|
2085 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
|
|
2086 llvm::MemoryBuffer::getFile(File);
|
|
2087 if (!FileBufOrErr)
|
|
2088 return true;
|
|
2089
|
|
2090 llvm::SourceMgr SM;
|
|
2091 Stream YAMLStream(FileBufOrErr.get()->getMemBufferRef(), SM);
|
|
2092 document_iterator I = YAMLStream.begin();
|
|
2093 if (I == YAMLStream.end())
|
|
2094 return true;
|
|
2095 Node *Root = I->getRoot();
|
|
2096 if (!Root)
|
|
2097 return true;
|
|
2098
|
|
2099 SequenceNode *SeqNode = dyn_cast<SequenceNode>(Root);
|
|
2100 if (!SeqNode)
|
|
2101 return true;
|
|
2102
|
|
2103 for (SequenceNode::iterator
|
|
2104 AI = SeqNode->begin(), AE = SeqNode->end(); AI != AE; ++AI) {
|
|
2105 MappingNode *MapNode = dyn_cast<MappingNode>(&*AI);
|
|
2106 if (!MapNode)
|
|
2107 continue;
|
|
2108 parseEdit(MapNode, Entries);
|
|
2109 }
|
|
2110
|
|
2111 return false;
|
|
2112 }
|
|
2113
|
|
2114 private:
|
|
2115 void parseEdit(llvm::yaml::MappingNode *Node,
|
|
2116 SmallVectorImpl<EditEntry> &Entries) {
|
|
2117 using namespace llvm::yaml;
|
|
2118 EditEntry Entry;
|
|
2119 bool Ignore = false;
|
|
2120
|
|
2121 for (MappingNode::iterator
|
|
2122 KVI = Node->begin(), KVE = Node->end(); KVI != KVE; ++KVI) {
|
|
2123 ScalarNode *KeyString = dyn_cast<ScalarNode>((*KVI).getKey());
|
|
2124 if (!KeyString)
|
|
2125 continue;
|
|
2126 SmallString<10> KeyStorage;
|
|
2127 StringRef Key = KeyString->getValue(KeyStorage);
|
|
2128
|
|
2129 ScalarNode *ValueString = dyn_cast<ScalarNode>((*KVI).getValue());
|
|
2130 if (!ValueString)
|
|
2131 continue;
|
|
2132 SmallString<64> ValueStorage;
|
|
2133 StringRef Val = ValueString->getValue(ValueStorage);
|
|
2134
|
|
2135 if (Key == "file") {
|
|
2136 auto FE = FileMgr.getFile(Val);
|
|
2137 if (FE)
|
|
2138 Entry.File = *FE;
|
|
2139 else
|
|
2140 Ignore = true;
|
|
2141 } else if (Key == "offset") {
|
|
2142 if (Val.getAsInteger(10, Entry.Offset))
|
|
2143 Ignore = true;
|
|
2144 } else if (Key == "remove") {
|
|
2145 if (Val.getAsInteger(10, Entry.RemoveLen))
|
|
2146 Ignore = true;
|
|
2147 } else if (Key == "text") {
|
|
2148 Entry.Text = std::string(Val);
|
|
2149 }
|
|
2150 }
|
|
2151
|
|
2152 if (!Ignore)
|
|
2153 Entries.push_back(Entry);
|
|
2154 }
|
|
2155 };
|
|
2156 } // end anonymous namespace
|
|
2157
|
|
2158 static bool reportDiag(const Twine &Err, DiagnosticsEngine &Diag) {
|
|
2159 Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
|
|
2160 << Err.str();
|
|
2161 return true;
|
|
2162 }
|
|
2163
|
|
2164 static std::string applyEditsToTemp(const FileEntry *FE,
|
|
2165 ArrayRef<EditEntry> Edits,
|
|
2166 FileManager &FileMgr,
|
|
2167 DiagnosticsEngine &Diag) {
|
|
2168 using namespace llvm::sys;
|
|
2169
|
|
2170 SourceManager SM(Diag, FileMgr);
|
|
2171 FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User);
|
|
2172 LangOptions LangOpts;
|
|
2173 edit::EditedSource Editor(SM, LangOpts);
|
|
2174 for (ArrayRef<EditEntry>::iterator
|
|
2175 I = Edits.begin(), E = Edits.end(); I != E; ++I) {
|
|
2176 const EditEntry &Entry = *I;
|
|
2177 assert(Entry.File == FE);
|
|
2178 SourceLocation Loc =
|
|
2179 SM.getLocForStartOfFile(FID).getLocWithOffset(Entry.Offset);
|
|
2180 CharSourceRange Range;
|
|
2181 if (Entry.RemoveLen != 0) {
|
|
2182 Range = CharSourceRange::getCharRange(Loc,
|
|
2183 Loc.getLocWithOffset(Entry.RemoveLen));
|
|
2184 }
|
|
2185
|
|
2186 edit::Commit commit(Editor);
|
|
2187 if (Range.isInvalid()) {
|
|
2188 commit.insert(Loc, Entry.Text);
|
|
2189 } else if (Entry.Text.empty()) {
|
|
2190 commit.remove(Range);
|
|
2191 } else {
|
|
2192 commit.replace(Range, Entry.Text);
|
|
2193 }
|
|
2194 Editor.commit(commit);
|
|
2195 }
|
|
2196
|
|
2197 Rewriter rewriter(SM, LangOpts);
|
|
2198 RewritesReceiver Rec(rewriter);
|
|
2199 Editor.applyRewrites(Rec, /*adjustRemovals=*/false);
|
|
2200
|
|
2201 const RewriteBuffer *Buf = rewriter.getRewriteBufferFor(FID);
|
|
2202 SmallString<512> NewText;
|
|
2203 llvm::raw_svector_ostream OS(NewText);
|
|
2204 Buf->write(OS);
|
|
2205
|
|
2206 SmallString<64> TempPath;
|
|
2207 int FD;
|
|
2208 if (fs::createTemporaryFile(path::filename(FE->getName()),
|
|
2209 path::extension(FE->getName()).drop_front(), FD,
|
|
2210 TempPath)) {
|
|
2211 reportDiag("Could not create file: " + TempPath.str(), Diag);
|
|
2212 return std::string();
|
|
2213 }
|
|
2214
|
|
2215 llvm::raw_fd_ostream TmpOut(FD, /*shouldClose=*/true);
|
|
2216 TmpOut.write(NewText.data(), NewText.size());
|
|
2217 TmpOut.close();
|
|
2218
|
|
2219 return std::string(TempPath.str());
|
|
2220 }
|
|
2221
|
|
2222 bool arcmt::getFileRemappingsFromFileList(
|
|
2223 std::vector<std::pair<std::string,std::string> > &remap,
|
|
2224 ArrayRef<StringRef> remapFiles,
|
|
2225 DiagnosticConsumer *DiagClient) {
|
|
2226 bool hasErrorOccurred = false;
|
|
2227
|
|
2228 FileSystemOptions FSOpts;
|
|
2229 FileManager FileMgr(FSOpts);
|
|
2230 RemapFileParser Parser(FileMgr);
|
|
2231
|
|
2232 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
|
2233 IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
|
2234 new DiagnosticsEngine(DiagID, new DiagnosticOptions,
|
|
2235 DiagClient, /*ShouldOwnClient=*/false));
|
|
2236
|
|
2237 typedef llvm::DenseMap<const FileEntry *, std::vector<EditEntry> >
|
|
2238 FileEditEntriesTy;
|
|
2239 FileEditEntriesTy FileEditEntries;
|
|
2240
|
|
2241 llvm::DenseSet<EditEntry> EntriesSet;
|
|
2242
|
|
2243 for (ArrayRef<StringRef>::iterator
|
|
2244 I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) {
|
|
2245 SmallVector<EditEntry, 16> Entries;
|
|
2246 if (Parser.parse(*I, Entries))
|
|
2247 continue;
|
|
2248
|
|
2249 for (SmallVectorImpl<EditEntry>::iterator
|
|
2250 EI = Entries.begin(), EE = Entries.end(); EI != EE; ++EI) {
|
|
2251 EditEntry &Entry = *EI;
|
|
2252 if (!Entry.File)
|
|
2253 continue;
|
|
2254 std::pair<llvm::DenseSet<EditEntry>::iterator, bool>
|
|
2255 Insert = EntriesSet.insert(Entry);
|
|
2256 if (!Insert.second)
|
|
2257 continue;
|
|
2258
|
|
2259 FileEditEntries[Entry.File].push_back(Entry);
|
|
2260 }
|
|
2261 }
|
|
2262
|
|
2263 for (FileEditEntriesTy::iterator
|
|
2264 I = FileEditEntries.begin(), E = FileEditEntries.end(); I != E; ++I) {
|
|
2265 std::string TempFile = applyEditsToTemp(I->first, I->second,
|
|
2266 FileMgr, *Diags);
|
|
2267 if (TempFile.empty()) {
|
|
2268 hasErrorOccurred = true;
|
|
2269 continue;
|
|
2270 }
|
|
2271
|
|
2272 remap.emplace_back(std::string(I->first->getName()), TempFile);
|
|
2273 }
|
|
2274
|
|
2275 return hasErrorOccurred;
|
|
2276 }
|