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