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"
36 using namespace clang;
37 using namespace arcmt;
43 enum CF_BRIDGING_KIND {
46 CF_BRIDGING_MAY_INCLUDE
49 void migrateDecl(
Decl *D);
51 void migrateProtocolConformance(
ASTContext &Ctx,
53 void CacheObjCNSIntegerTypedefed(
const TypedefDecl *TypedefDcl);
74 void AnnotateImplicitBridging(
ASTContext &Ctx);
76 CF_BRIDGING_KIND migrateAddFunctionAnnotation(
ASTContext &Ctx,
81 void migrateAddMethodAnnotation(
ASTContext &Ctx,
84 void inferDesignatedInitializers(
ASTContext &Ctx,
89 std::unique_ptr<RetainSummaryManager> Summaries;
93 unsigned ASTMigrateActions;
97 std::unique_ptr<NSAPI> NSAPIObj;
98 std::unique_ptr<edit::EditedSource> Editor;
104 bool FoundationIncluded;
107 llvm::StringSet<> AllowListFilenames;
117 ObjCMigrateASTConsumer(StringRef migrateDir,
unsigned astMigrateActions,
122 : MigrateDir(migrateDir), ASTMigrateActions(astMigrateActions),
123 NSIntegerTypedefed(nullptr), NSUIntegerTypedefed(nullptr),
124 Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),
125 IsOutputFile(isOutputFile), FoundationIncluded(
false) {
128 AllowListFilenames.insert(Val);
132 void Initialize(
ASTContext &Context)
override {
133 NSAPIObj.reset(
new NSAPI(Context));
147 void HandleTopLevelDeclInObjCContainer(
DeclGroupRef DG)
override {
148 ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
151 void HandleTranslationUnit(
ASTContext &Ctx)
override;
153 bool canModifyFile(StringRef Path) {
154 if (AllowListFilenames.empty())
156 return AllowListFilenames.find(llvm::sys::path::filename(Path)) !=
157 AllowListFilenames.end();
162 return canModifyFile(FE->getName());
164 bool canModifyFile(
FileID FID) {
170 bool canModify(
const Decl *D) {
174 return canModify(CatImpl->getCategoryDecl());
176 return canModify(Impl->getClassInterface());
178 return canModify(cast<Decl>(MD->getDeclContext()));
181 return canModifyFile(FID);
188 std::unique_ptr<FrontendAction> WrappedAction, StringRef migrateDir,
189 unsigned migrateAction)
191 ObjCMigAction(migrateAction), CompInst(nullptr) {
192 if (MigrateDir.empty())
196 std::unique_ptr<ASTConsumer>
201 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
203 Consumers.push_back(std::make_unique<ObjCMigrateASTConsumer>(
204 MigrateDir, ObjCMigAction, Remapper, CompInst->
getFileManager(), PPRec,
206 return std::make_unique<MultiplexConsumer>(std::move(Consumers));
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));
243 if (Receiver->getType()->isObjCBuiltinType())
257 bool ReceiverIsSuper =
276 PropertyDotString =
").";
279 PropertyDotString =
".";
280 PropertyDotString += Prop->
getName();
281 commit.
replace(SpaceRange, PropertyDotString);
290 PropertyDotString += Prop->
getName();
291 PropertyDotString +=
" =";
293 const Expr *RHS = Args[0];
303 if (colon && colon[0] ==
':')
304 PropertyDotString +=
" ";
315 ObjCMigrateASTConsumer &Consumer;
319 ObjCMigrator(ObjCMigrateASTConsumer &consumer,
ParentMap &PMap)
320 : Consumer(consumer), PMap(PMap) { }
322 bool shouldVisitTemplateInstantiations()
const {
return false; }
323 bool shouldWalkTypesOfTypeLocs()
const {
return false; }
329 Consumer.Editor->commit(commit);
335 Consumer.Editor->commit(commit);
340 rewriteToPropertyDotSyntax(E, Consumer.PP, *Consumer.NSAPIObj,
342 Consumer.Editor->commit(commit);
352 if (!TraverseStmt(SubStmt))
355 return WalkUpFromObjCMessageExpr(E);
360 ObjCMigrateASTConsumer &Consumer;
361 std::unique_ptr<ParentMap> PMap;
364 BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
366 bool shouldVisitTemplateInstantiations()
const {
return false; }
367 bool shouldWalkTypesOfTypeLocs()
const {
return false; }
369 bool TraverseStmt(
Stmt *S) {
371 ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
377 void ObjCMigrateASTConsumer::migrateDecl(
Decl *D) {
380 if (isa<ObjCMethodDecl>(D))
383 BodyMigrator(*this).TraverseDecl(D);
389 PropertyString +=
"(";
393 PropertyString +=
", ";
394 PropertyString +=
attr;
401 const char *argPtr = TypeString.c_str();
406 PropertyString += *argPtr;
410 PropertyString += *argPtr;
415 PropertyString += (*argPtr);
417 PropertyString +=
name;
422 PropertyString += *argPtr;
432 if (RetainableObject &&
450 else if (RetainableObject)
458 unsigned LengthOfPrefix,
459 bool Atomic,
bool UseNsIosOnlyMacro,
460 bool AvailabilityArgsMatch) {
462 bool LParenAdded =
false;
464 if (UseNsIosOnlyMacro && NS.
isMacroDefined(
"NS_NONATOMIC_IOSONLY")) {
465 PropertyString +=
"(NS_NONATOMIC_IOSONLY";
467 }
else if (!Atomic) {
468 PropertyString +=
"(nonatomic";
473 StringRef PropertyName(PropertyNameString);
474 if (LengthOfPrefix > 0) {
476 PropertyString +=
"(getter=";
480 PropertyString +=
", getter=";
481 PropertyString += PropertyNameString;
485 append_attr(PropertyString,
"readonly", LParenAdded);
490 if (PropertyName.equals(
"target") || PropertyName.contains(
"delegate") ||
491 PropertyName.contains(
"dataSource")) {
494 append_attr(PropertyString,
"assign", LParenAdded);
495 }
else if (!Setter) {
498 append_attr(PropertyString, MemoryManagementAttr, LParenAdded);
503 append_attr(PropertyString, MemoryManagementAttr, LParenAdded);
506 PropertyString +=
')';
508 if (!isa<TypedefType>(RT)) {
517 PropertyString +=
" ";
522 if (LengthOfPrefix > 0) {
525 StringRef PropertyNameStringRef(PropertyNameString);
526 PropertyNameStringRef = PropertyNameStringRef.drop_front(LengthOfPrefix);
527 PropertyNameString =
std::string(PropertyNameStringRef);
528 bool NoLowering = (
isUppercase(PropertyNameString[0]) &&
529 PropertyNameString.size() > 1 &&
532 PropertyNameString[0] =
toLowercase(PropertyNameString[0]);
537 PropertyNameString.c_str());
539 char LastChar = TypeString[TypeString.size()-1];
540 PropertyString += TypeString;
542 PropertyString +=
' ';
543 PropertyString += PropertyNameString;
551 EndGetterSelectorLoc),
553 if (Setter && AvailabilityArgsMatch) {
567 StringRef Name = CatDecl->getName();
568 return Name.endswith(
"Deprecated");
573 void ObjCMigrateASTConsumer::migrateObjCContainerDecl(
ASTContext &Ctx,
578 for (
auto *Method : D->
methods()) {
581 bool PropertyInferred = migrateProperty(Ctx, D, Method);
585 if (!PropertyInferred ||
588 migrateNsReturnsInnerPointer(Ctx, Method);
596 migratePropertyNsReturnsInnerPointer(Ctx, Prop);
608 bool HasAtleastOneRequiredProperty =
false;
610 for (
const auto *
Property : PDecl->instance_properties()) {
613 HasAtleastOneRequiredProperty =
true;
620 Property->getDeclName().getAsIdentifierInfo(),
624 if ((ClassProperty->getPropertyAttributes() !=
625 Property->getPropertyAttributes()) ||
635 bool HasAtleastOneRequiredMethod =
false;
637 if (PDecl->meth_begin() == PDecl->meth_end())
638 return HasAtleastOneRequiredProperty;
639 for (
const auto *MD : PDecl->methods()) {
640 if (MD->isImplicit())
648 HasAtleastOneRequiredMethod =
true;
659 return HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod;
670 if (Protocols.
empty()) {
672 for (
unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
673 ClassString += ConformingProtocols[i]->getNameAsString();
681 for (
unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
682 ClassString += ConformingProtocols[i]->getNameAsString();
695 StringRef UnsignedName = llvm::StringSwitch<StringRef>(NSIntegerName)
696 .Case(
"int8_t",
"uint8_t")
697 .Case(
"int16_t",
"uint16_t")
698 .Case(
"int32_t",
"uint32_t")
699 .Case(
"NSInteger",
"NSUInteger")
700 .Case(
"int64_t",
"uint64_t")
701 .Default(NSIntegerName);
708 StringRef NSIntegerName,
712 ClassString =
"typedef NS_OPTIONS(";
716 ClassString =
"typedef NS_ENUM(";
717 ClassString += NSIntegerName;
724 commit.
replace(R, ClassString);
728 if (EndOfEnumDclLoc.
isValid()) {
738 if (EndTypedefDclLoc.
isValid()) {
748 if (EndOfEnumDclLoc.
isValid()) {
763 bool IsNSIntegerType) {
765 assert(!DesignatedEnumType.
isNull()
766 &&
"rewriteToNSMacroDecl - underlying enum type is null");
770 std::string ClassString = IsNSIntegerType ?
"NS_ENUM(" :
"NS_OPTIONS(";
771 ClassString += TypeString;
781 commit.
replace(R, ClassString);
792 bool PowerOfTwo =
true;
793 bool AllHexdecimalEnumerator =
true;
794 uint64_t MaxPowerOfTwoVal = 0;
796 const Expr *InitExpr = Enumerator->getInitExpr();
799 AllHexdecimalEnumerator =
false;
803 if (
const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
804 if (BO->isShiftOp() || BO->isBitwiseOp())
807 uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
808 if (PowerOfTwo && EnumVal) {
809 if (!llvm::isPowerOf2_64(EnumVal))
811 else if (EnumVal > MaxPowerOfTwoVal)
812 MaxPowerOfTwoVal = EnumVal;
814 if (AllHexdecimalEnumerator && EnumVal) {
815 bool FoundHexdecimalEnumerator =
false;
821 FoundHexdecimalEnumerator =
822 (StringLit[0] ==
'0' && (
toLowercase(StringLit[1]) ==
'x'));
824 if (!FoundHexdecimalEnumerator)
825 AllHexdecimalEnumerator =
false;
828 return AllHexdecimalEnumerator || (PowerOfTwo && (MaxPowerOfTwoVal > 2));
831 void ObjCMigrateASTConsumer::migrateProtocolConformance(
ASTContext &Ctx,
834 if (!IDecl || ObjCProtocolDecls.empty() || IDecl->
isDeprecated())
843 if (!ExplicitProtocols.count(ProtDecl))
844 PotentialImplicitProtocols.push_back(ProtDecl);
846 if (PotentialImplicitProtocols.empty())
853 for (
unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)
855 PotentialImplicitProtocols[i]))
856 ConformingProtocols.push_back(PotentialImplicitProtocols[i]);
858 if (ConformingProtocols.empty())
864 for (
unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
867 for (
unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {
869 if (PDecl == TargetPDecl)
878 MinimalConformingProtocols.push_back(TargetPDecl);
880 if (MinimalConformingProtocols.empty())
885 Editor->commit(commit);
888 void ObjCMigrateASTConsumer::CacheObjCNSIntegerTypedefed(
892 if (NSAPIObj->isObjCNSIntegerType(qt))
893 NSIntegerTypedefed = TypedefDcl;
894 else if (NSAPIObj->isObjCNSUIntegerType(qt))
895 NSUIntegerTypedefed = TypedefDcl;
898 bool ObjCMigrateASTConsumer::migrateNSEnumDecl(
ASTContext &Ctx,
905 if (NSIntegerTypedefed) {
906 TypedefDcl = NSIntegerTypedefed;
907 NSIntegerTypedefed =
nullptr;
909 else if (NSUIntegerTypedefed) {
910 TypedefDcl = NSUIntegerTypedefed;
911 NSUIntegerTypedefed =
nullptr;
915 FileID FileIdOfTypedefDcl =
919 if (FileIdOfTypedefDcl != FileIdOfEnumDcl)
926 StringRef NSIntegerName = NSAPIObj->GetNSIntegralKind(qt);
928 if (NSIntegerName.empty()) {
931 if (EnumTy->getDecl() == EnumDcl) {
933 if (!InsertFoundation(Ctx, TypedefDcl->
getBeginLoc()))
937 Editor->commit(commit);
946 if (!InsertFoundation(Ctx, TypedefDcl->
getBeginLoc()))
950 commit, NSIntegerName, NSOptions);
951 Editor->commit(commit);
956 const ObjCMigrateASTConsumer &ASTC,
964 TypeLoc TL = TSInfo->getTypeLoc();
966 ClassString =
"instancetype";
971 ClassString +=
" (instancetype)";
974 commit.
replace(R, ClassString);
975 ASTC.Editor->commit(commit);
984 TypeLoc TL = TSInfo->getTypeLoc();
993 ClassString += IDecl->
getName(); ClassString +=
"*)";
996 commit.
replace(R, ClassString);
997 ASTC.Editor->commit(commit);
1000 void ObjCMigrateASTConsumer::migrateMethodInstanceType(
ASTContext &Ctx,
1007 switch (OIT_Family) {
1009 migrateFactoryMethod(Ctx, CDecl, OM);
1012 ClassName =
"NSArray";
1015 ClassName =
"NSDictionary";
1034 IDecl = CatDecl->getClassInterface();
1035 else if (
ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
1040 migrateFactoryMethod(Ctx, CDecl, OM);
1057 T = TD->getDecl()->getUnderlyingType();
1062 if (UPointeeT->isRecordType()) {
1081 const AvailabilityAttr *AA1 = dyn_cast<AvailabilityAttr>(At1);
1084 const AvailabilityAttr *AA2 = cast<AvailabilityAttr>(At2);
1086 VersionTuple Introduced1 = AA1->getIntroduced();
1087 VersionTuple Deprecated1 = AA1->getDeprecated();
1088 VersionTuple Obsoleted1 = AA1->getObsoleted();
1089 bool IsUnavailable1 = AA1->getUnavailable();
1090 VersionTuple Introduced2 = AA2->getIntroduced();
1091 VersionTuple Deprecated2 = AA2->getDeprecated();
1092 VersionTuple Obsoleted2 = AA2->getObsoleted();
1093 bool IsUnavailable2 = AA2->getUnavailable();
1097 IsUnavailable1 == IsUnavailable2);
1101 bool &AvailabilityArgsMatch) {
1103 for (
unsigned i = 0, e = Attrs1.size(); i != e; i++) {
1105 for (
unsigned j = 0, f = Attrs2.size(); j != f; j++) {
1110 if (AvailabilityArgsMatch)
1130 bool &AvailabilityArgsMatch) {
1135 AvailabilityArgsMatch =
true;
1139 if (
match && (Attrs2.size() > Attrs1.size()))
1154 bool ObjCMigrateASTConsumer::migrateProperty(
ASTContext &Ctx,
1178 unsigned LengthOfPrefix = 0;
1179 if (!SetterMethod) {
1181 StringRef getterNameString = getterName->
getName();
1182 bool IsPrefix = getterNameString.startswith(
"is");
1187 if (IsPrefix || getterNameString.startswith(
"get")) {
1188 LengthOfPrefix = (IsPrefix ? 2 : 3);
1189 const char *CGetterName = getterNameString.data() + LengthOfPrefix;
1194 if (CGetterName[0] &&
isUppercase(CGetterName[0])) {
1195 getterName = &Ctx.
Idents.
get(CGetterName);
1208 bool AvailabilityArgsMatch;
1224 (ASTMigrateActions &
1226 (ASTMigrateActions &
1228 AvailabilityArgsMatch);
1229 Editor->commit(commit);
1238 (ASTMigrateActions &
1240 (ASTMigrateActions &
1243 Editor->commit(commit);
1249 void ObjCMigrateASTConsumer::migrateNsReturnsInnerPointer(
ASTContext &Ctx,
1253 OM->
hasAttr<ObjCReturnsInnerPointerAttr>())
1258 !NSAPIObj->isMacroDefined(
"NS_RETURNS_INNER_POINTER"))
1263 Editor->commit(commit);
1266 void ObjCMigrateASTConsumer::migratePropertyNsReturnsInnerPointer(
ASTContext &Ctx,
1271 !NSAPIObj->isMacroDefined(
"NS_RETURNS_INNER_POINTER"))
1274 commit.
insertBefore(
P->getEndLoc(),
" NS_RETURNS_INNER_POINTER ");
1275 Editor->commit(commit);
1278 void ObjCMigrateASTConsumer::migrateAllMethodInstaceType(
ASTContext &Ctx,
1284 for (
auto *Method : CDecl->
methods()) {
1287 migrateMethodInstanceType(Ctx, CDecl, Method);
1291 void ObjCMigrateASTConsumer::migrateFactoryMethod(
ASTContext &Ctx,
1305 IDecl = CatDecl->getClassInterface();
1306 else if (
ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
1313 StringRef LoweredClassName(StringClassName);
1314 std::string StringLoweredClassName = LoweredClassName.lower();
1315 LoweredClassName = StringLoweredClassName;
1324 StringRef STRefMethodName(MethodName);
1326 if (STRefMethodName.startswith(
"standard"))
1327 len = strlen(
"standard");
1328 else if (STRefMethodName.startswith(
"shared"))
1329 len = strlen(
"shared");
1330 else if (STRefMethodName.startswith(
"default"))
1331 len = strlen(
"default");
1334 MethodName =
std::string(STRefMethodName.substr(len));
1336 std::string MethodNameSubStr = MethodName.substr(0, 3);
1337 StringRef MethodNamePrefix(MethodNameSubStr);
1338 std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower();
1339 MethodNamePrefix = StringLoweredMethodNamePrefix;
1340 size_t Ix = LoweredClassName.rfind(MethodNamePrefix);
1341 if (Ix == StringRef::npos)
1344 StringRef LoweredMethodName(MethodName);
1345 std::string StringLoweredMethodName = LoweredMethodName.lower();
1346 LoweredMethodName = StringLoweredMethodName;
1347 if (!LoweredMethodName.startswith(ClassNamePostfix))
1360 Ty = TD->getDecl()->getUnderlyingType();
1386 void ObjCMigrateASTConsumer::AnnotateImplicitBridging(
ASTContext &Ctx) {
1387 if (CFFunctionIBCandidates.empty())
1389 if (!NSAPIObj->isMacroDefined(
"CF_IMPLICIT_BRIDGING_ENABLED")) {
1390 CFFunctionIBCandidates.clear();
1395 const Decl *FirstFD = CFFunctionIBCandidates[0];
1396 const Decl *LastFD =
1397 CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1];
1398 const char *PragmaString =
"\nCF_IMPLICIT_BRIDGING_ENABLED\n\n";
1401 PragmaString =
"\n\nCF_IMPLICIT_BRIDGING_DISABLED\n";
1405 if (isa<FunctionDecl>(LastFD)) {
1415 Editor->commit(commit);
1417 CFFunctionIBCandidates.clear();
1420 void ObjCMigrateASTConsumer::migrateCFAnnotation(
ASTContext &Ctx,
const Decl *
Decl) {
1425 assert(CFFunctionIBCandidates.empty() &&
1426 "Cannot have audited functions/methods inside user "
1427 "provided CF_IMPLICIT_BRIDGING_ENABLE");
1433 CF_BRIDGING_KIND AuditKind = migrateAddFunctionAnnotation(Ctx, FuncDecl);
1434 if (AuditKind == CF_BRIDGING_ENABLE) {
1435 CFFunctionIBCandidates.push_back(
Decl);
1439 else if (AuditKind == CF_BRIDGING_MAY_INCLUDE) {
1440 if (!CFFunctionIBCandidates.empty()) {
1441 CFFunctionIBCandidates.push_back(
Decl);
1447 AnnotateImplicitBridging(Ctx);
1450 migrateAddMethodAnnotation(Ctx, cast<ObjCMethodDecl>(
Decl));
1451 AnnotateImplicitBridging(Ctx);
1455 void ObjCMigrateASTConsumer::AddCFAnnotations(
ASTContext &Ctx,
1458 bool ResultAnnotated) {
1460 if (!ResultAnnotated) {
1462 const char *AnnotationString =
nullptr;
1463 if (
Ret.getObjKind() == ObjKind::CF) {
1464 if (
Ret.isOwned() && NSAPIObj->isMacroDefined(
"CF_RETURNS_RETAINED"))
1465 AnnotationString =
" CF_RETURNS_RETAINED";
1466 else if (
Ret.notOwned() &&
1467 NSAPIObj->isMacroDefined(
"CF_RETURNS_NOT_RETAINED"))
1468 AnnotationString =
" CF_RETURNS_NOT_RETAINED";
1471 if (
Ret.isOwned() && NSAPIObj->isMacroDefined(
"NS_RETURNS_RETAINED"))
1472 AnnotationString =
" NS_RETURNS_RETAINED";
1475 if (AnnotationString) {
1478 Editor->commit(commit);
1483 pe = FuncDecl->
param_end(); pi != pe; ++pi, ++i) {
1487 !pd->
hasAttr<CFConsumedAttr>() &&
1488 NSAPIObj->isMacroDefined(
"CF_CONSUMED")) {
1491 Editor->commit(commit);
1493 !pd->
hasAttr<NSConsumedAttr>() &&
1494 NSAPIObj->isMacroDefined(
"NS_CONSUMED")) {
1497 Editor->commit(commit);
1502 ObjCMigrateASTConsumer::CF_BRIDGING_KIND
1503 ObjCMigrateASTConsumer::migrateAddFunctionAnnotation(
1507 return CF_BRIDGING_NONE;
1510 getSummaryManager(Ctx).getSummary(
AnyCall(FuncDecl));
1511 bool FuncIsReturnAnnotated = (FuncDecl->
hasAttr<CFReturnsRetainedAttr>() ||
1512 FuncDecl->
hasAttr<CFReturnsNotRetainedAttr>() ||
1513 FuncDecl->
hasAttr<NSReturnsRetainedAttr>() ||
1514 FuncDecl->
hasAttr<NSReturnsNotRetainedAttr>() ||
1515 FuncDecl->
hasAttr<NSReturnsAutoreleasedAttr>());
1518 if (FuncIsReturnAnnotated && FuncDecl->
getNumParams() == 0)
1519 return CF_BRIDGING_NONE;
1521 bool ReturnCFAudited =
false;
1522 if (!FuncIsReturnAnnotated) {
1524 if (
Ret.getObjKind() == ObjKind::CF &&
1525 (
Ret.isOwned() ||
Ret.notOwned()))
1526 ReturnCFAudited =
true;
1528 return CF_BRIDGING_NONE;
1533 bool ArgCFAudited =
false;
1535 pe = FuncDecl->
param_end(); pi != pe; ++pi, ++i) {
1541 ArgCFAudited =
true;
1543 ArgCFAudited =
true;
1547 AddCFAnnotations(Ctx, RS, FuncDecl, FuncIsReturnAnnotated);
1548 return CF_BRIDGING_NONE;
1552 if (ReturnCFAudited || ArgCFAudited)
1553 return CF_BRIDGING_ENABLE;
1555 return CF_BRIDGING_MAY_INCLUDE;
1558 void ObjCMigrateASTConsumer::migrateARCSafeAnnotation(
ASTContext &Ctx,
1560 if (!isa<ObjCInterfaceDecl>(CDecl) || CDecl->
isDeprecated())
1564 for (
const auto *Method : CDecl->
methods())
1565 migrateCFAnnotation(Ctx, Method);
1568 void ObjCMigrateASTConsumer::AddCFAnnotations(
ASTContext &Ctx,
1571 bool ResultAnnotated) {
1573 if (!ResultAnnotated) {
1575 const char *AnnotationString =
nullptr;
1576 if (
Ret.getObjKind() == ObjKind::CF) {
1577 if (
Ret.isOwned() && NSAPIObj->isMacroDefined(
"CF_RETURNS_RETAINED"))
1578 AnnotationString =
" CF_RETURNS_RETAINED";
1579 else if (
Ret.notOwned() &&
1580 NSAPIObj->isMacroDefined(
"CF_RETURNS_NOT_RETAINED"))
1581 AnnotationString =
" CF_RETURNS_NOT_RETAINED";
1594 if (
Ret.isOwned() && NSAPIObj->isMacroDefined(
"NS_RETURNS_RETAINED"))
1595 AnnotationString =
" NS_RETURNS_RETAINED";
1600 if (AnnotationString) {
1603 Editor->commit(commit);
1608 pe = MethodDecl->
param_end(); pi != pe; ++pi, ++i) {
1613 && !pd->
hasAttr<CFConsumedAttr>() &&
1614 NSAPIObj->isMacroDefined(
"CF_CONSUMED")) {
1617 Editor->commit(commit);
1622 void ObjCMigrateASTConsumer::migrateAddMethodAnnotation(
1629 getSummaryManager(Ctx).getSummary(
AnyCall(MethodDecl));
1631 bool MethodIsReturnAnnotated =
1632 (MethodDecl->
hasAttr<CFReturnsRetainedAttr>() ||
1633 MethodDecl->
hasAttr<CFReturnsNotRetainedAttr>() ||
1634 MethodDecl->
hasAttr<NSReturnsRetainedAttr>() ||
1635 MethodDecl->
hasAttr<NSReturnsNotRetainedAttr>() ||
1636 MethodDecl->
hasAttr<NSReturnsAutoreleasedAttr>());
1639 !MethodDecl->
hasAttr<NSConsumesSelfAttr>() &&
1642 NSAPIObj->isMacroDefined(
"NS_CONSUMES_SELF")) {
1645 Editor->commit(commit);
1649 if (MethodIsReturnAnnotated &&
1653 if (!MethodIsReturnAnnotated) {
1655 if ((
Ret.getObjKind() == ObjKind::CF ||
1657 (
Ret.isOwned() ||
Ret.notOwned())) {
1658 AddCFAnnotations(Ctx, RS, MethodDecl,
false);
1667 pe = MethodDecl->
param_end(); pi != pe; ++pi, ++i) {
1672 AddCFAnnotations(Ctx, RS, MethodDecl, MethodIsReturnAnnotated);
1681 bool shouldVisitTemplateInstantiations()
const {
return false; }
1682 bool shouldWalkTypesOfTypeLocs()
const {
return false; }
1695 return !SuperInitChecker().TraverseStmt(MD->
getBody());
1698 void ObjCMigrateASTConsumer::inferDesignatedInitializers(
1705 if (!NSAPIObj->isMacroDefined(
"NS_DESIGNATED_INITIALIZER"))
1709 if (MD->isDeprecated() ||
1710 MD->getMethodFamily() !=
OMF_init ||
1711 MD->isDesignatedInitializerForTheInterface())
1720 Editor->commit(commit);
1725 bool ObjCMigrateASTConsumer::InsertFoundation(
ASTContext &Ctx,
1727 if (FoundationIncluded)
1729 if (
Loc.isInvalid())
1731 auto *nsEnumId = &Ctx.
Idents.
get(
"NS_ENUM");
1733 FoundationIncluded =
true;
1738 commit.
insert(
Loc,
"#ifndef NS_ENUM\n@import Foundation;\n#endif\n");
1740 commit.
insert(
Loc,
"#ifndef NS_ENUM\n#import <Foundation/Foundation.h>\n#endif\n");
1741 Editor->commit(commit);
1742 FoundationIncluded =
true;
1764 llvm::raw_ostream &
OS;
1768 : SourceMgr(
SM),
OS(
OS) {
1771 ~JSONEditWriter()
override {
OS <<
"]\n"; }
1774 struct EntryWriter {
1776 llvm::raw_ostream &
OS;
1779 : SourceMgr(
SM),
OS(
OS) {
1793 llvm::sys::fs::make_absolute(Path);
1794 OS <<
" \"file\": \"";
1795 OS.write_escaped(Path.str()) <<
"\",\n";
1796 OS <<
" \"offset\": " <<
Offset <<
",\n";
1800 assert(
Range.isCharRange());
1801 std::pair<FileID, unsigned>
Begin =
1803 std::pair<FileID, unsigned>
End =
1806 assert(
Begin.second <=
End.second);
1807 unsigned Length =
End.second -
Begin.second;
1809 OS <<
" \"remove\": " << Length <<
",\n";
1812 void writeText(StringRef
Text) {
1813 OS <<
" \"text\": \"";
1814 OS.write_escaped(
Text) <<
"\",\n";
1819 EntryWriter Writer(SourceMgr, OS);
1820 Writer.writeLoc(
Loc);
1821 Writer.writeText(
Text);
1825 EntryWriter Writer(SourceMgr, OS);
1826 Writer.writeLoc(
Range.getBegin());
1827 Writer.writeRemove(
Range);
1828 Writer.writeText(
Text);
1832 EntryWriter Writer(SourceMgr, OS);
1833 Writer.writeLoc(
Range.getBegin());
1834 Writer.writeRemove(
Range);
1840 void ObjCMigrateASTConsumer::HandleTranslationUnit(
ASTContext &Ctx) {
1848 if (FileId.
isValid() && FileId != FID) {
1850 AnnotateImplicitBridging(Ctx);
1854 if (canModify(CDecl))
1855 migrateObjCContainerDecl(Ctx, CDecl);
1857 if (canModify(CatDecl))
1858 migrateObjCContainerDecl(Ctx, CatDecl);
1862 if (canModify(PDecl))
1863 migrateObjCContainerDecl(Ctx, PDecl);
1866 dyn_cast<ObjCImplementationDecl>(*D)) {
1869 migrateProtocolConformance(Ctx, ImpDecl);
1871 else if (
const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {
1878 const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N);
1879 if (migrateNSEnumDecl(Ctx, ED, TD) && TD)
1883 migrateNSEnumDecl(Ctx, ED,
nullptr);
1885 else if (
const TypedefDecl *TD = dyn_cast<TypedefDecl>(*D)) {
1893 if (
const EnumDecl *ED = dyn_cast<EnumDecl>(*N)) {
1894 if (canModify(ED)) {
1896 if (
const TypedefDecl *TDF = dyn_cast<TypedefDecl>(*N)) {
1898 if (migrateNSEnumDecl(Ctx, ED, TDF)) {
1900 CacheObjCNSIntegerTypedefed(TD);
1904 if (migrateNSEnumDecl(Ctx, ED, TD)) {
1910 CacheObjCNSIntegerTypedefed(TD);
1912 else if (
const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D)) {
1915 migrateCFAnnotation(Ctx, FD);
1919 bool CanModify = canModify(CDecl);
1923 migrateAllMethodInstaceType(Ctx, CDecl);
1927 migrateARCSafeAnnotation(Ctx, CDecl);
1931 ImplD = dyn_cast<ObjCImplementationDecl>(*D)) {
1934 inferDesignatedInitializers(Ctx, ImplD);
1938 AnnotateImplicitBridging(Ctx);
1943 llvm::raw_fd_ostream
OS(MigrateDir, EC, llvm::sys::fs::OF_None);
1952 Editor->applyRewrites(Writer);
1957 RewritesReceiver Rec(rewriter);
1958 Editor->applyRewrites(Rec);
1961 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
1967 llvm::raw_svector_ostream vecOS(newText);
1969 std::unique_ptr<llvm::MemoryBuffer> memBuf(
1970 llvm::MemoryBuffer::getMemBufferCopy(
1971 StringRef(newText.data(), newText.size()), file->getName()));
1974 Remapper.
remap(filePath.str(), std::move(memBuf));
1990 using namespace llvm::sys::fs;
1991 using namespace llvm::sys::path;
1993 std::vector<std::string> Filenames;
1994 if (DirPath.empty() || !is_directory(DirPath))
1998 directory_iterator DI = directory_iterator(DirPath, EC);
1999 directory_iterator DE;
2000 for (; !EC && DI != DE; DI = DI.increment(EC)) {
2001 if (is_regular_file(DI->path()))
2002 Filenames.push_back(
std::string(filename(DI->path())));
2008 std::unique_ptr<ASTConsumer>
2013 unsigned ObjCMTOpts = ObjCMTAction;
2024 std::vector<std::string> AllowList =
2026 return std::make_unique<ObjCMigrateASTConsumer>(
2036 unsigned RemoveLen = 0;
2042 template<>
struct DenseMapInfo<EditEntry> {
2057 static bool isEqual(
const EditEntry &LHS,
const EditEntry &RHS) {
2058 return LHS.File == RHS.File &&
2059 LHS.Offset == RHS.Offset &&
2060 LHS.RemoveLen == RHS.RemoveLen &&
2061 LHS.Text == RHS.Text;
2067 class RemapFileParser {
2071 RemapFileParser(
FileManager &FileMgr) : FileMgr(FileMgr) { }
2076 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
2077 llvm::MemoryBuffer::getFile(File);
2082 Stream YAMLStream(FileBufOrErr.get()->getMemBufferRef(),
SM);
2083 document_iterator I = YAMLStream.begin();
2084 if (I == YAMLStream.end())
2086 Node *Root = I->getRoot();
2090 SequenceNode *SeqNode = dyn_cast<SequenceNode>(Root);
2094 for (SequenceNode::iterator
2095 AI = SeqNode->begin(), AE = SeqNode->end(); AI != AE; ++AI) {
2096 MappingNode *MapNode = dyn_cast<MappingNode>(&*AI);
2099 parseEdit(MapNode, Entries);
2106 void parseEdit(llvm::yaml::MappingNode *
Node,
2110 bool Ignore =
false;
2112 for (MappingNode::iterator
2113 KVI =
Node->begin(), KVE =
Node->end(); KVI != KVE; ++KVI) {
2114 ScalarNode *KeyString = dyn_cast<ScalarNode>((*KVI).getKey());
2118 StringRef Key = KeyString->getValue(KeyStorage);
2120 ScalarNode *ValueString = dyn_cast<ScalarNode>((*KVI).getValue());
2124 StringRef Val = ValueString->getValue(ValueStorage);
2126 if (Key ==
"file") {
2131 }
else if (Key ==
"offset") {
2132 if (Val.getAsInteger(10, Entry.Offset))
2134 }
else if (Key ==
"remove") {
2135 if (Val.getAsInteger(10, Entry.RemoveLen))
2137 }
else if (Key ==
"text") {
2143 Entries.push_back(Entry);
2158 using namespace llvm::sys;
2165 I = Edits.begin(), E = Edits.end(); I != E; ++I) {
2166 const EditEntry &Entry = *I;
2167 assert(Entry.File == FE);
2169 SM.getLocForStartOfFile(FID).getLocWithOffset(Entry.Offset);
2171 if (Entry.RemoveLen != 0) {
2173 Loc.getLocWithOffset(Entry.RemoveLen));
2177 if (
Range.isInvalid()) {
2179 }
else if (Entry.Text.empty()) {
2188 RewritesReceiver Rec(rewriter);
2193 llvm::raw_svector_ostream
OS(NewText);
2198 if (fs::createTemporaryFile(path::filename(FE.
getName()),
2199 path::extension(FE.
getName()).drop_front(), FD,
2205 llvm::raw_fd_ostream TmpOut(FD,
true);
2206 TmpOut.write(NewText.data(), NewText.size());
2213 std::vector<std::pair<std::string,std::string> > &remap,
2216 bool hasErrorOccurred =
false;
2220 RemapFileParser
Parser(FileMgr);
2225 DiagClient,
false));
2227 typedef llvm::DenseMap<FileEntryRef, std::vector<EditEntry> >
2229 FileEditEntriesTy FileEditEntries;
2234 I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) {
2236 if (
Parser.parse(*I, Entries))
2240 EI = Entries.begin(), EE = Entries.end(); EI != EE; ++EI) {
2241 EditEntry &Entry = *EI;
2244 std::pair<llvm::DenseSet<EditEntry>::iterator,
bool>
2245 Insert = EntriesSet.insert(Entry);
2249 FileEditEntries[*Entry.File].push_back(Entry);
2253 for (FileEditEntriesTy::iterator
2254 I = FileEditEntries.begin(), E = FileEditEntries.end(); I != E; ++I) {
2257 if (TempFile.empty()) {
2258 hasErrorOccurred =
true;
2262 remap.emplace_back(
std::string(I->first.getName()), TempFile);
2265 return hasErrorOccurred;