Path: blob/main/contrib/llvm-project/clang/lib/ARCMigrate/ObjCMT.cpp
35236 views
//===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Transforms.h"9#include "clang/Analysis/RetainSummaryManager.h"10#include "clang/ARCMigrate/ARCMT.h"11#include "clang/ARCMigrate/ARCMTActions.h"12#include "clang/AST/ASTConsumer.h"13#include "clang/AST/ASTContext.h"14#include "clang/AST/Attr.h"15#include "clang/AST/NSAPI.h"16#include "clang/AST/ParentMap.h"17#include "clang/AST/RecursiveASTVisitor.h"18#include "clang/Analysis/DomainSpecific/CocoaConventions.h"19#include "clang/Basic/FileManager.h"20#include "clang/Edit/Commit.h"21#include "clang/Edit/EditedSource.h"22#include "clang/Edit/EditsReceiver.h"23#include "clang/Edit/Rewriters.h"24#include "clang/Frontend/CompilerInstance.h"25#include "clang/Frontend/MultiplexConsumer.h"26#include "clang/Lex/PPConditionalDirectiveRecord.h"27#include "clang/Lex/Preprocessor.h"28#include "clang/Rewrite/Core/Rewriter.h"29#include "llvm/ADT/SmallString.h"30#include "llvm/ADT/StringSet.h"31#include "llvm/Support/Path.h"32#include "llvm/Support/SourceMgr.h"33#include "llvm/Support/YAMLParser.h"3435using namespace clang;36using namespace arcmt;37using namespace ento;3839namespace {4041class ObjCMigrateASTConsumer : public ASTConsumer {42enum CF_BRIDGING_KIND {43CF_BRIDGING_NONE,44CF_BRIDGING_ENABLE,45CF_BRIDGING_MAY_INCLUDE46};4748void migrateDecl(Decl *D);49void migrateObjCContainerDecl(ASTContext &Ctx, ObjCContainerDecl *D);50void migrateProtocolConformance(ASTContext &Ctx,51const ObjCImplementationDecl *ImpDecl);52void CacheObjCNSIntegerTypedefed(const TypedefDecl *TypedefDcl);53bool migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl,54const TypedefDecl *TypedefDcl);55void migrateAllMethodInstaceType(ASTContext &Ctx, ObjCContainerDecl *CDecl);56void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl,57ObjCMethodDecl *OM);58bool migrateProperty(ASTContext &Ctx, ObjCContainerDecl *D, ObjCMethodDecl *OM);59void migrateNsReturnsInnerPointer(ASTContext &Ctx, ObjCMethodDecl *OM);60void migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, ObjCPropertyDecl *P);61void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl,62ObjCMethodDecl *OM,63ObjCInstanceTypeFamily OIT_Family = OIT_None);6465void migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl);66void AddCFAnnotations(ASTContext &Ctx,67const RetainSummary *RS,68const FunctionDecl *FuncDecl, bool ResultAnnotated);69void AddCFAnnotations(ASTContext &Ctx,70const RetainSummary *RS,71const ObjCMethodDecl *MethodDecl, bool ResultAnnotated);7273void AnnotateImplicitBridging(ASTContext &Ctx);7475CF_BRIDGING_KIND migrateAddFunctionAnnotation(ASTContext &Ctx,76const FunctionDecl *FuncDecl);7778void migrateARCSafeAnnotation(ASTContext &Ctx, ObjCContainerDecl *CDecl);7980void migrateAddMethodAnnotation(ASTContext &Ctx,81const ObjCMethodDecl *MethodDecl);8283void inferDesignatedInitializers(ASTContext &Ctx,84const ObjCImplementationDecl *ImplD);8586bool InsertFoundation(ASTContext &Ctx, SourceLocation Loc);8788std::unique_ptr<RetainSummaryManager> Summaries;8990public:91std::string MigrateDir;92unsigned ASTMigrateActions;93FileID FileId;94const TypedefDecl *NSIntegerTypedefed;95const TypedefDecl *NSUIntegerTypedefed;96std::unique_ptr<NSAPI> NSAPIObj;97std::unique_ptr<edit::EditedSource> Editor;98FileRemapper &Remapper;99FileManager &FileMgr;100const PPConditionalDirectiveRecord *PPRec;101Preprocessor &PP;102bool IsOutputFile;103bool FoundationIncluded;104llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls;105llvm::SmallVector<const Decl *, 8> CFFunctionIBCandidates;106llvm::StringSet<> AllowListFilenames;107108RetainSummaryManager &getSummaryManager(ASTContext &Ctx) {109if (!Summaries)110Summaries.reset(new RetainSummaryManager(Ctx,111/*TrackNSCFObjects=*/true,112/*trackOSObjects=*/false));113return *Summaries;114}115116ObjCMigrateASTConsumer(StringRef migrateDir, unsigned astMigrateActions,117FileRemapper &remapper, FileManager &fileMgr,118const PPConditionalDirectiveRecord *PPRec,119Preprocessor &PP, bool isOutputFile,120ArrayRef<std::string> AllowList)121: MigrateDir(migrateDir), ASTMigrateActions(astMigrateActions),122NSIntegerTypedefed(nullptr), NSUIntegerTypedefed(nullptr),123Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),124IsOutputFile(isOutputFile), FoundationIncluded(false) {125AllowListFilenames.insert(AllowList.begin(), AllowList.end());126}127128protected:129void Initialize(ASTContext &Context) override {130NSAPIObj.reset(new NSAPI(Context));131Editor.reset(new edit::EditedSource(Context.getSourceManager(),132Context.getLangOpts(),133PPRec));134}135136bool HandleTopLevelDecl(DeclGroupRef DG) override {137for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)138migrateDecl(*I);139return true;140}141void HandleInterestingDecl(DeclGroupRef DG) override {142// Ignore decls from the PCH.143}144void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override {145ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);146}147148void HandleTranslationUnit(ASTContext &Ctx) override;149150bool canModifyFile(StringRef Path) {151if (AllowListFilenames.empty())152return true;153return AllowListFilenames.contains(llvm::sys::path::filename(Path));154}155bool canModifyFile(OptionalFileEntryRef FE) {156if (!FE)157return false;158return canModifyFile(FE->getName());159}160bool canModifyFile(FileID FID) {161if (FID.isInvalid())162return false;163return canModifyFile(PP.getSourceManager().getFileEntryRefForID(FID));164}165166bool canModify(const Decl *D) {167if (!D)168return false;169if (const ObjCCategoryImplDecl *CatImpl = dyn_cast<ObjCCategoryImplDecl>(D))170return canModify(CatImpl->getCategoryDecl());171if (const ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(D))172return canModify(Impl->getClassInterface());173if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))174return canModify(cast<Decl>(MD->getDeclContext()));175176FileID FID = PP.getSourceManager().getFileID(D->getLocation());177return canModifyFile(FID);178}179};180181} // end anonymous namespace182183ObjCMigrateAction::ObjCMigrateAction(184std::unique_ptr<FrontendAction> WrappedAction, StringRef migrateDir,185unsigned migrateAction)186: WrapperFrontendAction(std::move(WrappedAction)), MigrateDir(migrateDir),187ObjCMigAction(migrateAction), CompInst(nullptr) {188if (MigrateDir.empty())189MigrateDir = "."; // user current directory if none is given.190}191192std::unique_ptr<ASTConsumer>193ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {194PPConditionalDirectiveRecord *195PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());196CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec));197std::vector<std::unique_ptr<ASTConsumer>> Consumers;198Consumers.push_back(WrapperFrontendAction::CreateASTConsumer(CI, InFile));199Consumers.push_back(std::make_unique<ObjCMigrateASTConsumer>(200MigrateDir, ObjCMigAction, Remapper, CompInst->getFileManager(), PPRec,201CompInst->getPreprocessor(), false, std::nullopt));202return std::make_unique<MultiplexConsumer>(std::move(Consumers));203}204205bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {206Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),207/*ignoreIfFilesChanged=*/true);208CompInst = &CI;209CI.getDiagnostics().setIgnoreAllWarnings(true);210return true;211}212213namespace {214// FIXME. This duplicates one in RewriteObjCFoundationAPI.cpp215bool subscriptOperatorNeedsParens(const Expr *FullExpr) {216const Expr* Expr = FullExpr->IgnoreImpCasts();217return !(isa<ArraySubscriptExpr>(Expr) || isa<CallExpr>(Expr) ||218isa<DeclRefExpr>(Expr) || isa<CXXNamedCastExpr>(Expr) ||219isa<CXXConstructExpr>(Expr) || isa<CXXThisExpr>(Expr) ||220isa<CXXTypeidExpr>(Expr) ||221isa<CXXUnresolvedConstructExpr>(Expr) ||222isa<ObjCMessageExpr>(Expr) || isa<ObjCPropertyRefExpr>(Expr) ||223isa<ObjCProtocolExpr>(Expr) || isa<MemberExpr>(Expr) ||224isa<ObjCIvarRefExpr>(Expr) || isa<ParenExpr>(FullExpr) ||225isa<ParenListExpr>(Expr) || isa<SizeOfPackExpr>(Expr));226}227228/// - Rewrite message expression for Objective-C setter and getters into229/// property-dot syntax.230bool rewriteToPropertyDotSyntax(const ObjCMessageExpr *Msg,231Preprocessor &PP,232const NSAPI &NS, edit::Commit &commit,233const ParentMap *PMap) {234if (!Msg || Msg->isImplicit() ||235(Msg->getReceiverKind() != ObjCMessageExpr::Instance &&236Msg->getReceiverKind() != ObjCMessageExpr::SuperInstance))237return false;238if (const Expr *Receiver = Msg->getInstanceReceiver())239if (Receiver->getType()->isObjCBuiltinType())240return false;241242const ObjCMethodDecl *Method = Msg->getMethodDecl();243if (!Method)244return false;245if (!Method->isPropertyAccessor())246return false;247248const ObjCPropertyDecl *Prop = Method->findPropertyDecl();249if (!Prop)250return false;251252SourceRange MsgRange = Msg->getSourceRange();253bool ReceiverIsSuper =254(Msg->getReceiverKind() == ObjCMessageExpr::SuperInstance);255// for 'super' receiver is nullptr.256const Expr *receiver = Msg->getInstanceReceiver();257bool NeedsParen =258ReceiverIsSuper ? false : subscriptOperatorNeedsParens(receiver);259bool IsGetter = (Msg->getNumArgs() == 0);260if (IsGetter) {261// Find space location range between receiver expression and getter method.262SourceLocation BegLoc =263ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getEndLoc();264BegLoc = PP.getLocForEndOfToken(BegLoc);265SourceLocation EndLoc = Msg->getSelectorLoc(0);266SourceRange SpaceRange(BegLoc, EndLoc);267std::string PropertyDotString;268// rewrite getter method expression into: receiver.property or269// (receiver).property270if (NeedsParen) {271commit.insertBefore(receiver->getBeginLoc(), "(");272PropertyDotString = ").";273}274else275PropertyDotString = ".";276PropertyDotString += Prop->getName();277commit.replace(SpaceRange, PropertyDotString);278279// remove '[' ']'280commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), "");281commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), "");282} else {283if (NeedsParen)284commit.insertWrap("(", receiver->getSourceRange(), ")");285std::string PropertyDotString = ".";286PropertyDotString += Prop->getName();287PropertyDotString += " =";288const Expr*const* Args = Msg->getArgs();289const Expr *RHS = Args[0];290if (!RHS)291return false;292SourceLocation BegLoc =293ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getEndLoc();294BegLoc = PP.getLocForEndOfToken(BegLoc);295SourceLocation EndLoc = RHS->getBeginLoc();296EndLoc = EndLoc.getLocWithOffset(-1);297const char *colon = PP.getSourceManager().getCharacterData(EndLoc);298// Add a space after '=' if there is no space between RHS and '='299if (colon && colon[0] == ':')300PropertyDotString += " ";301SourceRange Range(BegLoc, EndLoc);302commit.replace(Range, PropertyDotString);303// remove '[' ']'304commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), "");305commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), "");306}307return true;308}309310class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {311ObjCMigrateASTConsumer &Consumer;312ParentMap &PMap;313314public:315ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)316: Consumer(consumer), PMap(PMap) { }317318bool shouldVisitTemplateInstantiations() const { return false; }319bool shouldWalkTypesOfTypeLocs() const { return false; }320321bool VisitObjCMessageExpr(ObjCMessageExpr *E) {322if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Literals) {323edit::Commit commit(*Consumer.Editor);324edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);325Consumer.Editor->commit(commit);326}327328if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Subscripting) {329edit::Commit commit(*Consumer.Editor);330edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);331Consumer.Editor->commit(commit);332}333334if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_PropertyDotSyntax) {335edit::Commit commit(*Consumer.Editor);336rewriteToPropertyDotSyntax(E, Consumer.PP, *Consumer.NSAPIObj,337commit, &PMap);338Consumer.Editor->commit(commit);339}340341return true;342}343344bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {345// Do depth first; we want to rewrite the subexpressions first so that if346// we have to move expressions we will move them already rewritten.347for (Stmt *SubStmt : E->children())348if (!TraverseStmt(SubStmt))349return false;350351return WalkUpFromObjCMessageExpr(E);352}353};354355class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {356ObjCMigrateASTConsumer &Consumer;357std::unique_ptr<ParentMap> PMap;358359public:360BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }361362bool shouldVisitTemplateInstantiations() const { return false; }363bool shouldWalkTypesOfTypeLocs() const { return false; }364365bool TraverseStmt(Stmt *S) {366PMap.reset(new ParentMap(S));367ObjCMigrator(Consumer, *PMap).TraverseStmt(S);368return true;369}370};371} // end anonymous namespace372373void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {374if (!D)375return;376if (isa<ObjCMethodDecl>(D))377return; // Wait for the ObjC container declaration.378379BodyMigrator(*this).TraverseDecl(D);380}381382static void append_attr(std::string &PropertyString, const char *attr,383bool &LParenAdded) {384if (!LParenAdded) {385PropertyString += "(";386LParenAdded = true;387}388else389PropertyString += ", ";390PropertyString += attr;391}392393static394void MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString,395const std::string& TypeString,396const char *name) {397const char *argPtr = TypeString.c_str();398int paren = 0;399while (*argPtr) {400switch (*argPtr) {401case '(':402PropertyString += *argPtr;403paren++;404break;405case ')':406PropertyString += *argPtr;407paren--;408break;409case '^':410case '*':411PropertyString += (*argPtr);412if (paren == 1) {413PropertyString += name;414name = "";415}416break;417default:418PropertyString += *argPtr;419break;420}421argPtr++;422}423}424425static const char *PropertyMemoryAttribute(ASTContext &Context, QualType ArgType) {426Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();427bool RetainableObject = ArgType->isObjCRetainableType();428if (RetainableObject &&429(propertyLifetime == Qualifiers::OCL_Strong430|| propertyLifetime == Qualifiers::OCL_None)) {431if (const ObjCObjectPointerType *ObjPtrTy =432ArgType->getAs<ObjCObjectPointerType>()) {433ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();434if (IDecl &&435IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying")))436return "copy";437else438return "strong";439}440else if (ArgType->isBlockPointerType())441return "copy";442} else if (propertyLifetime == Qualifiers::OCL_Weak)443// TODO. More precise determination of 'weak' attribute requires444// looking into setter's implementation for backing weak ivar.445return "weak";446else if (RetainableObject)447return ArgType->isBlockPointerType() ? "copy" : "strong";448return nullptr;449}450451static void rewriteToObjCProperty(const ObjCMethodDecl *Getter,452const ObjCMethodDecl *Setter,453const NSAPI &NS, edit::Commit &commit,454unsigned LengthOfPrefix,455bool Atomic, bool UseNsIosOnlyMacro,456bool AvailabilityArgsMatch) {457ASTContext &Context = NS.getASTContext();458bool LParenAdded = false;459std::string PropertyString = "@property ";460if (UseNsIosOnlyMacro && NS.isMacroDefined("NS_NONATOMIC_IOSONLY")) {461PropertyString += "(NS_NONATOMIC_IOSONLY";462LParenAdded = true;463} else if (!Atomic) {464PropertyString += "(nonatomic";465LParenAdded = true;466}467468std::string PropertyNameString = Getter->getNameAsString();469StringRef PropertyName(PropertyNameString);470if (LengthOfPrefix > 0) {471if (!LParenAdded) {472PropertyString += "(getter=";473LParenAdded = true;474}475else476PropertyString += ", getter=";477PropertyString += PropertyNameString;478}479// Property with no setter may be suggested as a 'readonly' property.480if (!Setter)481append_attr(PropertyString, "readonly", LParenAdded);482483484// Short circuit 'delegate' properties that contain the name "delegate" or485// "dataSource", or have exact name "target" to have 'assign' attribute.486if (PropertyName == "target" || PropertyName.contains("delegate") ||487PropertyName.contains("dataSource")) {488QualType QT = Getter->getReturnType();489if (!QT->isRealType())490append_attr(PropertyString, "assign", LParenAdded);491} else if (!Setter) {492QualType ResType = Context.getCanonicalType(Getter->getReturnType());493if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ResType))494append_attr(PropertyString, MemoryManagementAttr, LParenAdded);495} else {496const ParmVarDecl *argDecl = *Setter->param_begin();497QualType ArgType = Context.getCanonicalType(argDecl->getType());498if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ArgType))499append_attr(PropertyString, MemoryManagementAttr, LParenAdded);500}501if (LParenAdded)502PropertyString += ')';503QualType RT = Getter->getReturnType();504if (!RT->getAs<TypedefType>()) {505// strip off any ARC lifetime qualifier.506QualType CanResultTy = Context.getCanonicalType(RT);507if (CanResultTy.getQualifiers().hasObjCLifetime()) {508Qualifiers Qs = CanResultTy.getQualifiers();509Qs.removeObjCLifetime();510RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs);511}512}513PropertyString += " ";514PrintingPolicy SubPolicy(Context.getPrintingPolicy());515SubPolicy.SuppressStrongLifetime = true;516SubPolicy.SuppressLifetimeQualifiers = true;517std::string TypeString = RT.getAsString(SubPolicy);518if (LengthOfPrefix > 0) {519// property name must strip off "is" and lower case the first character520// after that; e.g. isContinuous will become continuous.521StringRef PropertyNameStringRef(PropertyNameString);522PropertyNameStringRef = PropertyNameStringRef.drop_front(LengthOfPrefix);523PropertyNameString = std::string(PropertyNameStringRef);524bool NoLowering = (isUppercase(PropertyNameString[0]) &&525PropertyNameString.size() > 1 &&526isUppercase(PropertyNameString[1]));527if (!NoLowering)528PropertyNameString[0] = toLowercase(PropertyNameString[0]);529}530if (RT->isBlockPointerType() || RT->isFunctionPointerType())531MigrateBlockOrFunctionPointerTypeVariable(PropertyString,532TypeString,533PropertyNameString.c_str());534else {535char LastChar = TypeString[TypeString.size()-1];536PropertyString += TypeString;537if (LastChar != '*')538PropertyString += ' ';539PropertyString += PropertyNameString;540}541SourceLocation StartGetterSelectorLoc = Getter->getSelectorStartLoc();542Selector GetterSelector = Getter->getSelector();543544SourceLocation EndGetterSelectorLoc =545StartGetterSelectorLoc.getLocWithOffset(GetterSelector.getNameForSlot(0).size());546commit.replace(CharSourceRange::getCharRange(Getter->getBeginLoc(),547EndGetterSelectorLoc),548PropertyString);549if (Setter && AvailabilityArgsMatch) {550SourceLocation EndLoc = Setter->getDeclaratorEndLoc();551// Get location past ';'552EndLoc = EndLoc.getLocWithOffset(1);553SourceLocation BeginOfSetterDclLoc = Setter->getBeginLoc();554// FIXME. This assumes that setter decl; is immediately preceded by eoln.555// It is trying to remove the setter method decl. line entirely.556BeginOfSetterDclLoc = BeginOfSetterDclLoc.getLocWithOffset(-1);557commit.remove(SourceRange(BeginOfSetterDclLoc, EndLoc));558}559}560561static bool IsCategoryNameWithDeprecatedSuffix(ObjCContainerDecl *D) {562if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(D)) {563StringRef Name = CatDecl->getName();564return Name.ends_with("Deprecated");565}566return false;567}568569void ObjCMigrateASTConsumer::migrateObjCContainerDecl(ASTContext &Ctx,570ObjCContainerDecl *D) {571if (D->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(D))572return;573574for (auto *Method : D->methods()) {575if (Method->isDeprecated())576continue;577bool PropertyInferred = migrateProperty(Ctx, D, Method);578// If a property is inferred, do not attempt to attach NS_RETURNS_INNER_POINTER to579// the getter method as it ends up on the property itself which we don't want580// to do unless -objcmt-returns-innerpointer-property option is on.581if (!PropertyInferred ||582(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty))583if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)584migrateNsReturnsInnerPointer(Ctx, Method);585}586if (!(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty))587return;588589for (auto *Prop : D->instance_properties()) {590if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&591!Prop->isDeprecated())592migratePropertyNsReturnsInnerPointer(Ctx, Prop);593}594}595596static bool597ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,598const ObjCImplementationDecl *ImpDecl,599const ObjCInterfaceDecl *IDecl,600ObjCProtocolDecl *Protocol) {601// In auto-synthesis, protocol properties are not synthesized. So,602// a conforming protocol must have its required properties declared603// in class interface.604bool HasAtleastOneRequiredProperty = false;605if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition())606for (const auto *Property : PDecl->instance_properties()) {607if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional)608continue;609HasAtleastOneRequiredProperty = true;610DeclContext::lookup_result R = IDecl->lookup(Property->getDeclName());611if (R.empty()) {612// Relax the rule and look into class's implementation for a synthesize613// or dynamic declaration. Class is implementing a property coming from614// another protocol. This still makes the target protocol as conforming.615if (!ImpDecl->FindPropertyImplDecl(616Property->getDeclName().getAsIdentifierInfo(),617Property->getQueryKind()))618return false;619} else if (auto *ClassProperty = R.find_first<ObjCPropertyDecl>()) {620if ((ClassProperty->getPropertyAttributes() !=621Property->getPropertyAttributes()) ||622!Ctx.hasSameType(ClassProperty->getType(), Property->getType()))623return false;624} else625return false;626}627628// At this point, all required properties in this protocol conform to those629// declared in the class.630// Check that class implements the required methods of the protocol too.631bool HasAtleastOneRequiredMethod = false;632if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) {633if (PDecl->meth_begin() == PDecl->meth_end())634return HasAtleastOneRequiredProperty;635for (const auto *MD : PDecl->methods()) {636if (MD->isImplicit())637continue;638if (MD->getImplementationControl() == ObjCImplementationControl::Optional)639continue;640DeclContext::lookup_result R = ImpDecl->lookup(MD->getDeclName());641if (R.empty())642return false;643bool match = false;644HasAtleastOneRequiredMethod = true;645for (NamedDecl *ND : R)646if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(ND))647if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {648match = true;649break;650}651if (!match)652return false;653}654}655return HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod;656}657658static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl,659llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols,660const NSAPI &NS, edit::Commit &commit) {661const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols();662std::string ClassString;663SourceLocation EndLoc =664IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation();665666if (Protocols.empty()) {667ClassString = '<';668for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {669ClassString += ConformingProtocols[i]->getNameAsString();670if (i != (e-1))671ClassString += ", ";672}673ClassString += "> ";674}675else {676ClassString = ", ";677for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {678ClassString += ConformingProtocols[i]->getNameAsString();679if (i != (e-1))680ClassString += ", ";681}682ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1;683EndLoc = *PL;684}685686commit.insertAfterToken(EndLoc, ClassString);687return true;688}689690static StringRef GetUnsignedName(StringRef NSIntegerName) {691StringRef UnsignedName = llvm::StringSwitch<StringRef>(NSIntegerName)692.Case("int8_t", "uint8_t")693.Case("int16_t", "uint16_t")694.Case("int32_t", "uint32_t")695.Case("NSInteger", "NSUInteger")696.Case("int64_t", "uint64_t")697.Default(NSIntegerName);698return UnsignedName;699}700701static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,702const TypedefDecl *TypedefDcl,703const NSAPI &NS, edit::Commit &commit,704StringRef NSIntegerName,705bool NSOptions) {706std::string ClassString;707if (NSOptions) {708ClassString = "typedef NS_OPTIONS(";709ClassString += GetUnsignedName(NSIntegerName);710}711else {712ClassString = "typedef NS_ENUM(";713ClassString += NSIntegerName;714}715ClassString += ", ";716717ClassString += TypedefDcl->getIdentifier()->getName();718ClassString += ')';719SourceRange R(EnumDcl->getBeginLoc(), EnumDcl->getBeginLoc());720commit.replace(R, ClassString);721SourceLocation EndOfEnumDclLoc = EnumDcl->getEndLoc();722EndOfEnumDclLoc = trans::findSemiAfterLocation(EndOfEnumDclLoc,723NS.getASTContext(), /*IsDecl*/true);724if (EndOfEnumDclLoc.isValid()) {725SourceRange EnumDclRange(EnumDcl->getBeginLoc(), EndOfEnumDclLoc);726commit.insertFromRange(TypedefDcl->getBeginLoc(), EnumDclRange);727}728else729return false;730731SourceLocation EndTypedefDclLoc = TypedefDcl->getEndLoc();732EndTypedefDclLoc = trans::findSemiAfterLocation(EndTypedefDclLoc,733NS.getASTContext(), /*IsDecl*/true);734if (EndTypedefDclLoc.isValid()) {735SourceRange TDRange(TypedefDcl->getBeginLoc(), EndTypedefDclLoc);736commit.remove(TDRange);737}738else739return false;740741EndOfEnumDclLoc =742trans::findLocationAfterSemi(EnumDcl->getEndLoc(), NS.getASTContext(),743/*IsDecl*/ true);744if (EndOfEnumDclLoc.isValid()) {745SourceLocation BeginOfEnumDclLoc = EnumDcl->getBeginLoc();746// FIXME. This assumes that enum decl; is immediately preceded by eoln.747// It is trying to remove the enum decl. lines entirely.748BeginOfEnumDclLoc = BeginOfEnumDclLoc.getLocWithOffset(-1);749commit.remove(SourceRange(BeginOfEnumDclLoc, EndOfEnumDclLoc));750return true;751}752return false;753}754755static void rewriteToNSMacroDecl(ASTContext &Ctx,756const EnumDecl *EnumDcl,757const TypedefDecl *TypedefDcl,758const NSAPI &NS, edit::Commit &commit,759bool IsNSIntegerType) {760QualType DesignatedEnumType = EnumDcl->getIntegerType();761assert(!DesignatedEnumType.isNull()762&& "rewriteToNSMacroDecl - underlying enum type is null");763764PrintingPolicy Policy(Ctx.getPrintingPolicy());765std::string TypeString = DesignatedEnumType.getAsString(Policy);766std::string ClassString = IsNSIntegerType ? "NS_ENUM(" : "NS_OPTIONS(";767ClassString += TypeString;768ClassString += ", ";769770ClassString += TypedefDcl->getIdentifier()->getName();771ClassString += ") ";772SourceLocation EndLoc = EnumDcl->getBraceRange().getBegin();773if (EndLoc.isInvalid())774return;775CharSourceRange R =776CharSourceRange::getCharRange(EnumDcl->getBeginLoc(), EndLoc);777commit.replace(R, ClassString);778// This is to remove spaces between '}' and typedef name.779SourceLocation StartTypedefLoc = EnumDcl->getEndLoc();780StartTypedefLoc = StartTypedefLoc.getLocWithOffset(+1);781SourceLocation EndTypedefLoc = TypedefDcl->getEndLoc();782783commit.remove(SourceRange(StartTypedefLoc, EndTypedefLoc));784}785786static bool UseNSOptionsMacro(Preprocessor &PP, ASTContext &Ctx,787const EnumDecl *EnumDcl) {788bool PowerOfTwo = true;789bool AllHexdecimalEnumerator = true;790uint64_t MaxPowerOfTwoVal = 0;791for (auto *Enumerator : EnumDcl->enumerators()) {792const Expr *InitExpr = Enumerator->getInitExpr();793if (!InitExpr) {794PowerOfTwo = false;795AllHexdecimalEnumerator = false;796continue;797}798InitExpr = InitExpr->IgnoreParenCasts();799if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))800if (BO->isShiftOp() || BO->isBitwiseOp())801return true;802803uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();804if (PowerOfTwo && EnumVal) {805if (!llvm::isPowerOf2_64(EnumVal))806PowerOfTwo = false;807else if (EnumVal > MaxPowerOfTwoVal)808MaxPowerOfTwoVal = EnumVal;809}810if (AllHexdecimalEnumerator && EnumVal) {811bool FoundHexdecimalEnumerator = false;812SourceLocation EndLoc = Enumerator->getEndLoc();813Token Tok;814if (!PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true))815if (Tok.isLiteral() && Tok.getLength() > 2) {816if (const char *StringLit = Tok.getLiteralData())817FoundHexdecimalEnumerator =818(StringLit[0] == '0' && (toLowercase(StringLit[1]) == 'x'));819}820if (!FoundHexdecimalEnumerator)821AllHexdecimalEnumerator = false;822}823}824return AllHexdecimalEnumerator || (PowerOfTwo && (MaxPowerOfTwoVal > 2));825}826827void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,828const ObjCImplementationDecl *ImpDecl) {829const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();830if (!IDecl || ObjCProtocolDecls.empty() || IDecl->isDeprecated())831return;832// Find all implicit conforming protocols for this class833// and make them explicit.834llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols;835Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols);836llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols;837838for (ObjCProtocolDecl *ProtDecl : ObjCProtocolDecls)839if (!ExplicitProtocols.count(ProtDecl))840PotentialImplicitProtocols.push_back(ProtDecl);841842if (PotentialImplicitProtocols.empty())843return;844845// go through list of non-optional methods and properties in each protocol846// in the PotentialImplicitProtocols list. If class implements every one of the847// methods and properties, then this class conforms to this protocol.848llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols;849for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)850if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl,851PotentialImplicitProtocols[i]))852ConformingProtocols.push_back(PotentialImplicitProtocols[i]);853854if (ConformingProtocols.empty())855return;856857// Further reduce number of conforming protocols. If protocol P1 is in the list858// protocol P2 (P2<P1>), No need to include P1.859llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols;860for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {861bool DropIt = false;862ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i];863for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {864ObjCProtocolDecl *PDecl = ConformingProtocols[i1];865if (PDecl == TargetPDecl)866continue;867if (PDecl->lookupProtocolNamed(868TargetPDecl->getDeclName().getAsIdentifierInfo())) {869DropIt = true;870break;871}872}873if (!DropIt)874MinimalConformingProtocols.push_back(TargetPDecl);875}876if (MinimalConformingProtocols.empty())877return;878edit::Commit commit(*Editor);879rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols,880*NSAPIObj, commit);881Editor->commit(commit);882}883884void ObjCMigrateASTConsumer::CacheObjCNSIntegerTypedefed(885const TypedefDecl *TypedefDcl) {886887QualType qt = TypedefDcl->getTypeSourceInfo()->getType();888if (NSAPIObj->isObjCNSIntegerType(qt))889NSIntegerTypedefed = TypedefDcl;890else if (NSAPIObj->isObjCNSUIntegerType(qt))891NSUIntegerTypedefed = TypedefDcl;892}893894bool ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx,895const EnumDecl *EnumDcl,896const TypedefDecl *TypedefDcl) {897if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() ||898EnumDcl->isDeprecated())899return false;900if (!TypedefDcl) {901if (NSIntegerTypedefed) {902TypedefDcl = NSIntegerTypedefed;903NSIntegerTypedefed = nullptr;904}905else if (NSUIntegerTypedefed) {906TypedefDcl = NSUIntegerTypedefed;907NSUIntegerTypedefed = nullptr;908}909else910return false;911FileID FileIdOfTypedefDcl =912PP.getSourceManager().getFileID(TypedefDcl->getLocation());913FileID FileIdOfEnumDcl =914PP.getSourceManager().getFileID(EnumDcl->getLocation());915if (FileIdOfTypedefDcl != FileIdOfEnumDcl)916return false;917}918if (TypedefDcl->isDeprecated())919return false;920921QualType qt = TypedefDcl->getTypeSourceInfo()->getType();922StringRef NSIntegerName = NSAPIObj->GetNSIntegralKind(qt);923924if (NSIntegerName.empty()) {925// Also check for typedef enum {...} TD;926if (const EnumType *EnumTy = qt->getAs<EnumType>()) {927if (EnumTy->getDecl() == EnumDcl) {928bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl);929if (!InsertFoundation(Ctx, TypedefDcl->getBeginLoc()))930return false;931edit::Commit commit(*Editor);932rewriteToNSMacroDecl(Ctx, EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);933Editor->commit(commit);934return true;935}936}937return false;938}939940// We may still use NS_OPTIONS based on what we find in the enumertor list.941bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl);942if (!InsertFoundation(Ctx, TypedefDcl->getBeginLoc()))943return false;944edit::Commit commit(*Editor);945bool Res = rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj,946commit, NSIntegerName, NSOptions);947Editor->commit(commit);948return Res;949}950951static void ReplaceWithInstancetype(ASTContext &Ctx,952const ObjCMigrateASTConsumer &ASTC,953ObjCMethodDecl *OM) {954if (OM->getReturnType() == Ctx.getObjCInstanceType())955return; // already has instancetype.956957SourceRange R;958std::string ClassString;959if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) {960TypeLoc TL = TSInfo->getTypeLoc();961R = SourceRange(TL.getBeginLoc(), TL.getEndLoc());962ClassString = "instancetype";963}964else {965R = SourceRange(OM->getBeginLoc(), OM->getBeginLoc());966ClassString = OM->isInstanceMethod() ? '-' : '+';967ClassString += " (instancetype)";968}969edit::Commit commit(*ASTC.Editor);970commit.replace(R, ClassString);971ASTC.Editor->commit(commit);972}973974static void ReplaceWithClasstype(const ObjCMigrateASTConsumer &ASTC,975ObjCMethodDecl *OM) {976ObjCInterfaceDecl *IDecl = OM->getClassInterface();977SourceRange R;978std::string ClassString;979if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) {980TypeLoc TL = TSInfo->getTypeLoc();981R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); {982ClassString = std::string(IDecl->getName());983ClassString += "*";984}985}986else {987R = SourceRange(OM->getBeginLoc(), OM->getBeginLoc());988ClassString = "+ (";989ClassString += IDecl->getName(); ClassString += "*)";990}991edit::Commit commit(*ASTC.Editor);992commit.replace(R, ClassString);993ASTC.Editor->commit(commit);994}995996void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx,997ObjCContainerDecl *CDecl,998ObjCMethodDecl *OM) {999ObjCInstanceTypeFamily OIT_Family =1000Selector::getInstTypeMethodFamily(OM->getSelector());10011002std::string ClassName;1003switch (OIT_Family) {1004case OIT_None:1005migrateFactoryMethod(Ctx, CDecl, OM);1006return;1007case OIT_Array:1008ClassName = "NSArray";1009break;1010case OIT_Dictionary:1011ClassName = "NSDictionary";1012break;1013case OIT_Singleton:1014migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton);1015return;1016case OIT_Init:1017if (OM->getReturnType()->isObjCIdType())1018ReplaceWithInstancetype(Ctx, *this, OM);1019return;1020case OIT_ReturnsSelf:1021migrateFactoryMethod(Ctx, CDecl, OM, OIT_ReturnsSelf);1022return;1023}1024if (!OM->getReturnType()->isObjCIdType())1025return;10261027ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);1028if (!IDecl) {1029if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))1030IDecl = CatDecl->getClassInterface();1031else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))1032IDecl = ImpDecl->getClassInterface();1033}1034if (!IDecl ||1035!IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) {1036migrateFactoryMethod(Ctx, CDecl, OM);1037return;1038}1039ReplaceWithInstancetype(Ctx, *this, OM);1040}10411042static bool TypeIsInnerPointer(QualType T) {1043if (!T->isAnyPointerType())1044return false;1045if (T->isObjCObjectPointerType() || T->isObjCBuiltinType() ||1046T->isBlockPointerType() || T->isFunctionPointerType() ||1047ento::coreFoundation::isCFObjectRef(T))1048return false;1049// Also, typedef-of-pointer-to-incomplete-struct is something that we assume1050// is not an innter pointer type.1051QualType OrigT = T;1052while (const auto *TD = T->getAs<TypedefType>())1053T = TD->getDecl()->getUnderlyingType();1054if (OrigT == T || !T->isPointerType())1055return true;1056const PointerType* PT = T->getAs<PointerType>();1057QualType UPointeeT = PT->getPointeeType().getUnqualifiedType();1058if (UPointeeT->isRecordType()) {1059const RecordType *RecordTy = UPointeeT->getAs<RecordType>();1060if (!RecordTy->getDecl()->isCompleteDefinition())1061return false;1062}1063return true;1064}10651066/// Check whether the two versions match.1067static bool versionsMatch(const VersionTuple &X, const VersionTuple &Y) {1068return (X == Y);1069}10701071/// AvailabilityAttrsMatch - This routine checks that if comparing two1072/// availability attributes, all their components match. It returns1073/// true, if not dealing with availability or when all components of1074/// availability attributes match. This routine is only called when1075/// the attributes are of the same kind.1076static bool AvailabilityAttrsMatch(Attr *At1, Attr *At2) {1077const AvailabilityAttr *AA1 = dyn_cast<AvailabilityAttr>(At1);1078if (!AA1)1079return true;1080const AvailabilityAttr *AA2 = cast<AvailabilityAttr>(At2);10811082VersionTuple Introduced1 = AA1->getIntroduced();1083VersionTuple Deprecated1 = AA1->getDeprecated();1084VersionTuple Obsoleted1 = AA1->getObsoleted();1085bool IsUnavailable1 = AA1->getUnavailable();1086VersionTuple Introduced2 = AA2->getIntroduced();1087VersionTuple Deprecated2 = AA2->getDeprecated();1088VersionTuple Obsoleted2 = AA2->getObsoleted();1089bool IsUnavailable2 = AA2->getUnavailable();1090return (versionsMatch(Introduced1, Introduced2) &&1091versionsMatch(Deprecated1, Deprecated2) &&1092versionsMatch(Obsoleted1, Obsoleted2) &&1093IsUnavailable1 == IsUnavailable2);1094}10951096static bool MatchTwoAttributeLists(const AttrVec &Attrs1, const AttrVec &Attrs2,1097bool &AvailabilityArgsMatch) {1098// This list is very small, so this need not be optimized.1099for (unsigned i = 0, e = Attrs1.size(); i != e; i++) {1100bool match = false;1101for (unsigned j = 0, f = Attrs2.size(); j != f; j++) {1102// Matching attribute kind only. Except for Availability attributes,1103// we are not getting into details of the attributes. For all practical purposes1104// this is sufficient.1105if (Attrs1[i]->getKind() == Attrs2[j]->getKind()) {1106if (AvailabilityArgsMatch)1107AvailabilityArgsMatch = AvailabilityAttrsMatch(Attrs1[i], Attrs2[j]);1108match = true;1109break;1110}1111}1112if (!match)1113return false;1114}1115return true;1116}11171118/// AttributesMatch - This routine checks list of attributes for two1119/// decls. It returns false, if there is a mismatch in kind of1120/// attributes seen in the decls. It returns true if the two decls1121/// have list of same kind of attributes. Furthermore, when there1122/// are availability attributes in the two decls, it sets the1123/// AvailabilityArgsMatch to false if availability attributes have1124/// different versions, etc.1125static bool AttributesMatch(const Decl *Decl1, const Decl *Decl2,1126bool &AvailabilityArgsMatch) {1127if (!Decl1->hasAttrs() || !Decl2->hasAttrs()) {1128AvailabilityArgsMatch = (Decl1->hasAttrs() == Decl2->hasAttrs());1129return true;1130}1131AvailabilityArgsMatch = true;1132const AttrVec &Attrs1 = Decl1->getAttrs();1133const AttrVec &Attrs2 = Decl2->getAttrs();1134bool match = MatchTwoAttributeLists(Attrs1, Attrs2, AvailabilityArgsMatch);1135if (match && (Attrs2.size() > Attrs1.size()))1136return MatchTwoAttributeLists(Attrs2, Attrs1, AvailabilityArgsMatch);1137return match;1138}11391140static bool IsValidIdentifier(ASTContext &Ctx,1141const char *Name) {1142if (!isAsciiIdentifierStart(Name[0]))1143return false;1144std::string NameString = Name;1145NameString[0] = toLowercase(NameString[0]);1146const IdentifierInfo *II = &Ctx.Idents.get(NameString);1147return II->getTokenID() == tok::identifier;1148}11491150bool ObjCMigrateASTConsumer::migrateProperty(ASTContext &Ctx,1151ObjCContainerDecl *D,1152ObjCMethodDecl *Method) {1153if (Method->isPropertyAccessor() || !Method->isInstanceMethod() ||1154Method->param_size() != 0)1155return false;1156// Is this method candidate to be a getter?1157QualType GRT = Method->getReturnType();1158if (GRT->isVoidType())1159return false;11601161Selector GetterSelector = Method->getSelector();1162ObjCInstanceTypeFamily OIT_Family =1163Selector::getInstTypeMethodFamily(GetterSelector);11641165if (OIT_Family != OIT_None)1166return false;11671168const IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);1169Selector SetterSelector =1170SelectorTable::constructSetterSelector(PP.getIdentifierTable(),1171PP.getSelectorTable(),1172getterName);1173ObjCMethodDecl *SetterMethod = D->getInstanceMethod(SetterSelector);1174unsigned LengthOfPrefix = 0;1175if (!SetterMethod) {1176// try a different naming convention for getter: isXxxxx1177StringRef getterNameString = getterName->getName();1178bool IsPrefix = getterNameString.starts_with("is");1179// Note that we don't want to change an isXXX method of retainable object1180// type to property (readonly or otherwise).1181if (IsPrefix && GRT->isObjCRetainableType())1182return false;1183if (IsPrefix || getterNameString.starts_with("get")) {1184LengthOfPrefix = (IsPrefix ? 2 : 3);1185const char *CGetterName = getterNameString.data() + LengthOfPrefix;1186// Make sure that first character after "is" or "get" prefix can1187// start an identifier.1188if (!IsValidIdentifier(Ctx, CGetterName))1189return false;1190if (CGetterName[0] && isUppercase(CGetterName[0])) {1191getterName = &Ctx.Idents.get(CGetterName);1192SetterSelector =1193SelectorTable::constructSetterSelector(PP.getIdentifierTable(),1194PP.getSelectorTable(),1195getterName);1196SetterMethod = D->getInstanceMethod(SetterSelector);1197}1198}1199}12001201if (SetterMethod) {1202if ((ASTMigrateActions & FrontendOptions::ObjCMT_ReadwriteProperty) == 0)1203return false;1204bool AvailabilityArgsMatch;1205if (SetterMethod->isDeprecated() ||1206!AttributesMatch(Method, SetterMethod, AvailabilityArgsMatch))1207return false;12081209// Is this a valid setter, matching the target getter?1210QualType SRT = SetterMethod->getReturnType();1211if (!SRT->isVoidType())1212return false;1213const ParmVarDecl *argDecl = *SetterMethod->param_begin();1214QualType ArgType = argDecl->getType();1215if (!Ctx.hasSameUnqualifiedType(ArgType, GRT))1216return false;1217edit::Commit commit(*Editor);1218rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit,1219LengthOfPrefix,1220(ASTMigrateActions &1221FrontendOptions::ObjCMT_AtomicProperty) != 0,1222(ASTMigrateActions &1223FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0,1224AvailabilityArgsMatch);1225Editor->commit(commit);1226return true;1227}1228else if (ASTMigrateActions & FrontendOptions::ObjCMT_ReadonlyProperty) {1229// Try a non-void method with no argument (and no setter or property of same name1230// as a 'readonly' property.1231edit::Commit commit(*Editor);1232rewriteToObjCProperty(Method, nullptr /*SetterMethod*/, *NSAPIObj, commit,1233LengthOfPrefix,1234(ASTMigrateActions &1235FrontendOptions::ObjCMT_AtomicProperty) != 0,1236(ASTMigrateActions &1237FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0,1238/*AvailabilityArgsMatch*/false);1239Editor->commit(commit);1240return true;1241}1242return false;1243}12441245void ObjCMigrateASTConsumer::migrateNsReturnsInnerPointer(ASTContext &Ctx,1246ObjCMethodDecl *OM) {1247if (OM->isImplicit() ||1248!OM->isInstanceMethod() ||1249OM->hasAttr<ObjCReturnsInnerPointerAttr>())1250return;12511252QualType RT = OM->getReturnType();1253if (!TypeIsInnerPointer(RT) ||1254!NSAPIObj->isMacroDefined("NS_RETURNS_INNER_POINTER"))1255return;12561257edit::Commit commit(*Editor);1258commit.insertBefore(OM->getEndLoc(), " NS_RETURNS_INNER_POINTER");1259Editor->commit(commit);1260}12611262void ObjCMigrateASTConsumer::migratePropertyNsReturnsInnerPointer(ASTContext &Ctx,1263ObjCPropertyDecl *P) {1264QualType T = P->getType();12651266if (!TypeIsInnerPointer(T) ||1267!NSAPIObj->isMacroDefined("NS_RETURNS_INNER_POINTER"))1268return;1269edit::Commit commit(*Editor);1270commit.insertBefore(P->getEndLoc(), " NS_RETURNS_INNER_POINTER ");1271Editor->commit(commit);1272}12731274void ObjCMigrateASTConsumer::migrateAllMethodInstaceType(ASTContext &Ctx,1275ObjCContainerDecl *CDecl) {1276if (CDecl->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(CDecl))1277return;12781279// migrate methods which can have instancetype as their result type.1280for (auto *Method : CDecl->methods()) {1281if (Method->isDeprecated())1282continue;1283migrateMethodInstanceType(Ctx, CDecl, Method);1284}1285}12861287void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx,1288ObjCContainerDecl *CDecl,1289ObjCMethodDecl *OM,1290ObjCInstanceTypeFamily OIT_Family) {1291if (OM->isInstanceMethod() ||1292OM->getReturnType() == Ctx.getObjCInstanceType() ||1293!OM->getReturnType()->isObjCIdType())1294return;12951296// Candidate factory methods are + (id) NaMeXXX : ... which belong to a class1297// NSYYYNamE with matching names be at least 3 characters long.1298ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);1299if (!IDecl) {1300if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))1301IDecl = CatDecl->getClassInterface();1302else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))1303IDecl = ImpDecl->getClassInterface();1304}1305if (!IDecl)1306return;13071308std::string StringClassName = std::string(IDecl->getName());1309StringRef LoweredClassName(StringClassName);1310std::string StringLoweredClassName = LoweredClassName.lower();1311LoweredClassName = StringLoweredClassName;13121313const IdentifierInfo *MethodIdName =1314OM->getSelector().getIdentifierInfoForSlot(0);1315// Handle method with no name at its first selector slot; e.g. + (id):(int)x.1316if (!MethodIdName)1317return;13181319std::string MethodName = std::string(MethodIdName->getName());1320if (OIT_Family == OIT_Singleton || OIT_Family == OIT_ReturnsSelf) {1321StringRef STRefMethodName(MethodName);1322size_t len = 0;1323if (STRefMethodName.starts_with("standard"))1324len = strlen("standard");1325else if (STRefMethodName.starts_with("shared"))1326len = strlen("shared");1327else if (STRefMethodName.starts_with("default"))1328len = strlen("default");1329else1330return;1331MethodName = std::string(STRefMethodName.substr(len));1332}1333std::string MethodNameSubStr = MethodName.substr(0, 3);1334StringRef MethodNamePrefix(MethodNameSubStr);1335std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower();1336MethodNamePrefix = StringLoweredMethodNamePrefix;1337size_t Ix = LoweredClassName.rfind(MethodNamePrefix);1338if (Ix == StringRef::npos)1339return;1340std::string ClassNamePostfix = std::string(LoweredClassName.substr(Ix));1341StringRef LoweredMethodName(MethodName);1342std::string StringLoweredMethodName = LoweredMethodName.lower();1343LoweredMethodName = StringLoweredMethodName;1344if (!LoweredMethodName.starts_with(ClassNamePostfix))1345return;1346if (OIT_Family == OIT_ReturnsSelf)1347ReplaceWithClasstype(*this, OM);1348else1349ReplaceWithInstancetype(Ctx, *this, OM);1350}13511352static bool IsVoidStarType(QualType Ty) {1353if (!Ty->isPointerType())1354return false;13551356// Is the type void*?1357const PointerType* PT = Ty->castAs<PointerType>();1358if (PT->getPointeeType().getUnqualifiedType()->isVoidType())1359return true;1360return IsVoidStarType(PT->getPointeeType());1361}13621363/// AuditedType - This routine audits the type AT and returns false if it is one of known1364/// CF object types or of the "void *" variety. It returns true if we don't care about the type1365/// such as a non-pointer or pointers which have no ownership issues (such as "int *").1366static bool AuditedType (QualType AT) {1367if (!AT->isAnyPointerType() && !AT->isBlockPointerType())1368return true;1369// FIXME. There isn't much we can say about CF pointer type; or is there?1370if (ento::coreFoundation::isCFObjectRef(AT) ||1371IsVoidStarType(AT) ||1372// If an ObjC object is type, assuming that it is not a CF function and1373// that it is an un-audited function.1374AT->isObjCObjectPointerType() || AT->isObjCBuiltinType())1375return false;1376// All other pointers are assumed audited as harmless.1377return true;1378}13791380void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) {1381if (CFFunctionIBCandidates.empty())1382return;1383if (!NSAPIObj->isMacroDefined("CF_IMPLICIT_BRIDGING_ENABLED")) {1384CFFunctionIBCandidates.clear();1385FileId = FileID();1386return;1387}1388// Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED1389const Decl *FirstFD = CFFunctionIBCandidates[0];1390const Decl *LastFD =1391CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1];1392const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n";1393edit::Commit commit(*Editor);1394commit.insertBefore(FirstFD->getBeginLoc(), PragmaString);1395PragmaString = "\n\nCF_IMPLICIT_BRIDGING_DISABLED\n";1396SourceLocation EndLoc = LastFD->getEndLoc();1397// get location just past end of function location.1398EndLoc = PP.getLocForEndOfToken(EndLoc);1399if (isa<FunctionDecl>(LastFD)) {1400// For Methods, EndLoc points to the ending semcolon. So,1401// not of these extra work is needed.1402Token Tok;1403// get locaiton of token that comes after end of function.1404bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true);1405if (!Failed)1406EndLoc = Tok.getLocation();1407}1408commit.insertAfterToken(EndLoc, PragmaString);1409Editor->commit(commit);1410FileId = FileID();1411CFFunctionIBCandidates.clear();1412}14131414void ObjCMigrateASTConsumer::migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl) {1415if (Decl->isDeprecated())1416return;14171418if (Decl->hasAttr<CFAuditedTransferAttr>()) {1419assert(CFFunctionIBCandidates.empty() &&1420"Cannot have audited functions/methods inside user "1421"provided CF_IMPLICIT_BRIDGING_ENABLE");1422return;1423}14241425// Finction must be annotated first.1426if (const FunctionDecl *FuncDecl = dyn_cast<FunctionDecl>(Decl)) {1427CF_BRIDGING_KIND AuditKind = migrateAddFunctionAnnotation(Ctx, FuncDecl);1428if (AuditKind == CF_BRIDGING_ENABLE) {1429CFFunctionIBCandidates.push_back(Decl);1430if (FileId.isInvalid())1431FileId = PP.getSourceManager().getFileID(Decl->getLocation());1432}1433else if (AuditKind == CF_BRIDGING_MAY_INCLUDE) {1434if (!CFFunctionIBCandidates.empty()) {1435CFFunctionIBCandidates.push_back(Decl);1436if (FileId.isInvalid())1437FileId = PP.getSourceManager().getFileID(Decl->getLocation());1438}1439}1440else1441AnnotateImplicitBridging(Ctx);1442}1443else {1444migrateAddMethodAnnotation(Ctx, cast<ObjCMethodDecl>(Decl));1445AnnotateImplicitBridging(Ctx);1446}1447}14481449void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx,1450const RetainSummary *RS,1451const FunctionDecl *FuncDecl,1452bool ResultAnnotated) {1453// Annotate function.1454if (!ResultAnnotated) {1455RetEffect Ret = RS->getRetEffect();1456const char *AnnotationString = nullptr;1457if (Ret.getObjKind() == ObjKind::CF) {1458if (Ret.isOwned() && NSAPIObj->isMacroDefined("CF_RETURNS_RETAINED"))1459AnnotationString = " CF_RETURNS_RETAINED";1460else if (Ret.notOwned() &&1461NSAPIObj->isMacroDefined("CF_RETURNS_NOT_RETAINED"))1462AnnotationString = " CF_RETURNS_NOT_RETAINED";1463}1464else if (Ret.getObjKind() == ObjKind::ObjC) {1465if (Ret.isOwned() && NSAPIObj->isMacroDefined("NS_RETURNS_RETAINED"))1466AnnotationString = " NS_RETURNS_RETAINED";1467}14681469if (AnnotationString) {1470edit::Commit commit(*Editor);1471commit.insertAfterToken(FuncDecl->getEndLoc(), AnnotationString);1472Editor->commit(commit);1473}1474}1475unsigned i = 0;1476for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),1477pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {1478const ParmVarDecl *pd = *pi;1479ArgEffect AE = RS->getArg(i);1480if (AE.getKind() == DecRef && AE.getObjKind() == ObjKind::CF &&1481!pd->hasAttr<CFConsumedAttr>() &&1482NSAPIObj->isMacroDefined("CF_CONSUMED")) {1483edit::Commit commit(*Editor);1484commit.insertBefore(pd->getLocation(), "CF_CONSUMED ");1485Editor->commit(commit);1486} else if (AE.getKind() == DecRef && AE.getObjKind() == ObjKind::ObjC &&1487!pd->hasAttr<NSConsumedAttr>() &&1488NSAPIObj->isMacroDefined("NS_CONSUMED")) {1489edit::Commit commit(*Editor);1490commit.insertBefore(pd->getLocation(), "NS_CONSUMED ");1491Editor->commit(commit);1492}1493}1494}14951496ObjCMigrateASTConsumer::CF_BRIDGING_KIND1497ObjCMigrateASTConsumer::migrateAddFunctionAnnotation(1498ASTContext &Ctx,1499const FunctionDecl *FuncDecl) {1500if (FuncDecl->hasBody())1501return CF_BRIDGING_NONE;15021503const RetainSummary *RS =1504getSummaryManager(Ctx).getSummary(AnyCall(FuncDecl));1505bool FuncIsReturnAnnotated = (FuncDecl->hasAttr<CFReturnsRetainedAttr>() ||1506FuncDecl->hasAttr<CFReturnsNotRetainedAttr>() ||1507FuncDecl->hasAttr<NSReturnsRetainedAttr>() ||1508FuncDecl->hasAttr<NSReturnsNotRetainedAttr>() ||1509FuncDecl->hasAttr<NSReturnsAutoreleasedAttr>());15101511// Trivial case of when function is annotated and has no argument.1512if (FuncIsReturnAnnotated && FuncDecl->getNumParams() == 0)1513return CF_BRIDGING_NONE;15141515bool ReturnCFAudited = false;1516if (!FuncIsReturnAnnotated) {1517RetEffect Ret = RS->getRetEffect();1518if (Ret.getObjKind() == ObjKind::CF &&1519(Ret.isOwned() || Ret.notOwned()))1520ReturnCFAudited = true;1521else if (!AuditedType(FuncDecl->getReturnType()))1522return CF_BRIDGING_NONE;1523}15241525// At this point result type is audited for potential inclusion.1526unsigned i = 0;1527bool ArgCFAudited = false;1528for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),1529pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {1530const ParmVarDecl *pd = *pi;1531ArgEffect AE = RS->getArg(i);1532if ((AE.getKind() == DecRef /*CFConsumed annotated*/ ||1533AE.getKind() == IncRef) && AE.getObjKind() == ObjKind::CF) {1534if (AE.getKind() == DecRef && !pd->hasAttr<CFConsumedAttr>())1535ArgCFAudited = true;1536else if (AE.getKind() == IncRef)1537ArgCFAudited = true;1538} else {1539QualType AT = pd->getType();1540if (!AuditedType(AT)) {1541AddCFAnnotations(Ctx, RS, FuncDecl, FuncIsReturnAnnotated);1542return CF_BRIDGING_NONE;1543}1544}1545}1546if (ReturnCFAudited || ArgCFAudited)1547return CF_BRIDGING_ENABLE;15481549return CF_BRIDGING_MAY_INCLUDE;1550}15511552void ObjCMigrateASTConsumer::migrateARCSafeAnnotation(ASTContext &Ctx,1553ObjCContainerDecl *CDecl) {1554if (!isa<ObjCInterfaceDecl>(CDecl) || CDecl->isDeprecated())1555return;15561557// migrate methods which can have instancetype as their result type.1558for (const auto *Method : CDecl->methods())1559migrateCFAnnotation(Ctx, Method);1560}15611562void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx,1563const RetainSummary *RS,1564const ObjCMethodDecl *MethodDecl,1565bool ResultAnnotated) {1566// Annotate function.1567if (!ResultAnnotated) {1568RetEffect Ret = RS->getRetEffect();1569const char *AnnotationString = nullptr;1570if (Ret.getObjKind() == ObjKind::CF) {1571if (Ret.isOwned() && NSAPIObj->isMacroDefined("CF_RETURNS_RETAINED"))1572AnnotationString = " CF_RETURNS_RETAINED";1573else if (Ret.notOwned() &&1574NSAPIObj->isMacroDefined("CF_RETURNS_NOT_RETAINED"))1575AnnotationString = " CF_RETURNS_NOT_RETAINED";1576}1577else if (Ret.getObjKind() == ObjKind::ObjC) {1578ObjCMethodFamily OMF = MethodDecl->getMethodFamily();1579switch (OMF) {1580case clang::OMF_alloc:1581case clang::OMF_new:1582case clang::OMF_copy:1583case clang::OMF_init:1584case clang::OMF_mutableCopy:1585break;15861587default:1588if (Ret.isOwned() && NSAPIObj->isMacroDefined("NS_RETURNS_RETAINED"))1589AnnotationString = " NS_RETURNS_RETAINED";1590break;1591}1592}15931594if (AnnotationString) {1595edit::Commit commit(*Editor);1596commit.insertBefore(MethodDecl->getEndLoc(), AnnotationString);1597Editor->commit(commit);1598}1599}1600unsigned i = 0;1601for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(),1602pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) {1603const ParmVarDecl *pd = *pi;1604ArgEffect AE = RS->getArg(i);1605if (AE.getKind() == DecRef1606&& AE.getObjKind() == ObjKind::CF1607&& !pd->hasAttr<CFConsumedAttr>() &&1608NSAPIObj->isMacroDefined("CF_CONSUMED")) {1609edit::Commit commit(*Editor);1610commit.insertBefore(pd->getLocation(), "CF_CONSUMED ");1611Editor->commit(commit);1612}1613}1614}16151616void ObjCMigrateASTConsumer::migrateAddMethodAnnotation(1617ASTContext &Ctx,1618const ObjCMethodDecl *MethodDecl) {1619if (MethodDecl->hasBody() || MethodDecl->isImplicit())1620return;16211622const RetainSummary *RS =1623getSummaryManager(Ctx).getSummary(AnyCall(MethodDecl));16241625bool MethodIsReturnAnnotated =1626(MethodDecl->hasAttr<CFReturnsRetainedAttr>() ||1627MethodDecl->hasAttr<CFReturnsNotRetainedAttr>() ||1628MethodDecl->hasAttr<NSReturnsRetainedAttr>() ||1629MethodDecl->hasAttr<NSReturnsNotRetainedAttr>() ||1630MethodDecl->hasAttr<NSReturnsAutoreleasedAttr>());16311632if (RS->getReceiverEffect().getKind() == DecRef &&1633!MethodDecl->hasAttr<NSConsumesSelfAttr>() &&1634MethodDecl->getMethodFamily() != OMF_init &&1635MethodDecl->getMethodFamily() != OMF_release &&1636NSAPIObj->isMacroDefined("NS_CONSUMES_SELF")) {1637edit::Commit commit(*Editor);1638commit.insertBefore(MethodDecl->getEndLoc(), " NS_CONSUMES_SELF");1639Editor->commit(commit);1640}16411642// Trivial case of when function is annotated and has no argument.1643if (MethodIsReturnAnnotated &&1644(MethodDecl->param_begin() == MethodDecl->param_end()))1645return;16461647if (!MethodIsReturnAnnotated) {1648RetEffect Ret = RS->getRetEffect();1649if ((Ret.getObjKind() == ObjKind::CF ||1650Ret.getObjKind() == ObjKind::ObjC) &&1651(Ret.isOwned() || Ret.notOwned())) {1652AddCFAnnotations(Ctx, RS, MethodDecl, false);1653return;1654} else if (!AuditedType(MethodDecl->getReturnType()))1655return;1656}16571658// At this point result type is either annotated or audited.1659unsigned i = 0;1660for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(),1661pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) {1662const ParmVarDecl *pd = *pi;1663ArgEffect AE = RS->getArg(i);1664if ((AE.getKind() == DecRef && !pd->hasAttr<CFConsumedAttr>()) ||1665AE.getKind() == IncRef || !AuditedType(pd->getType())) {1666AddCFAnnotations(Ctx, RS, MethodDecl, MethodIsReturnAnnotated);1667return;1668}1669}1670}16711672namespace {1673class SuperInitChecker : public RecursiveASTVisitor<SuperInitChecker> {1674public:1675bool shouldVisitTemplateInstantiations() const { return false; }1676bool shouldWalkTypesOfTypeLocs() const { return false; }16771678bool VisitObjCMessageExpr(ObjCMessageExpr *E) {1679if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) {1680if (E->getMethodFamily() == OMF_init)1681return false;1682}1683return true;1684}1685};1686} // end anonymous namespace16871688static bool hasSuperInitCall(const ObjCMethodDecl *MD) {1689return !SuperInitChecker().TraverseStmt(MD->getBody());1690}16911692void ObjCMigrateASTConsumer::inferDesignatedInitializers(1693ASTContext &Ctx,1694const ObjCImplementationDecl *ImplD) {16951696const ObjCInterfaceDecl *IFace = ImplD->getClassInterface();1697if (!IFace || IFace->hasDesignatedInitializers())1698return;1699if (!NSAPIObj->isMacroDefined("NS_DESIGNATED_INITIALIZER"))1700return;17011702for (const auto *MD : ImplD->instance_methods()) {1703if (MD->isDeprecated() ||1704MD->getMethodFamily() != OMF_init ||1705MD->isDesignatedInitializerForTheInterface())1706continue;1707const ObjCMethodDecl *IFaceM = IFace->getMethod(MD->getSelector(),1708/*isInstance=*/true);1709if (!IFaceM)1710continue;1711if (hasSuperInitCall(MD)) {1712edit::Commit commit(*Editor);1713commit.insert(IFaceM->getEndLoc(), " NS_DESIGNATED_INITIALIZER");1714Editor->commit(commit);1715}1716}1717}17181719bool ObjCMigrateASTConsumer::InsertFoundation(ASTContext &Ctx,1720SourceLocation Loc) {1721if (FoundationIncluded)1722return true;1723if (Loc.isInvalid())1724return false;1725auto *nsEnumId = &Ctx.Idents.get("NS_ENUM");1726if (PP.getMacroDefinitionAtLoc(nsEnumId, Loc)) {1727FoundationIncluded = true;1728return true;1729}1730edit::Commit commit(*Editor);1731if (Ctx.getLangOpts().Modules)1732commit.insert(Loc, "#ifndef NS_ENUM\n@import Foundation;\n#endif\n");1733else1734commit.insert(Loc, "#ifndef NS_ENUM\n#import <Foundation/Foundation.h>\n#endif\n");1735Editor->commit(commit);1736FoundationIncluded = true;1737return true;1738}17391740namespace {17411742class RewritesReceiver : public edit::EditsReceiver {1743Rewriter &Rewrite;17441745public:1746RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }17471748void insert(SourceLocation loc, StringRef text) override {1749Rewrite.InsertText(loc, text);1750}1751void replace(CharSourceRange range, StringRef text) override {1752Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);1753}1754};17551756class JSONEditWriter : public edit::EditsReceiver {1757SourceManager &SourceMgr;1758llvm::raw_ostream &OS;17591760public:1761JSONEditWriter(SourceManager &SM, llvm::raw_ostream &OS)1762: SourceMgr(SM), OS(OS) {1763OS << "[\n";1764}1765~JSONEditWriter() override { OS << "]\n"; }17661767private:1768struct EntryWriter {1769SourceManager &SourceMgr;1770llvm::raw_ostream &OS;17711772EntryWriter(SourceManager &SM, llvm::raw_ostream &OS)1773: SourceMgr(SM), OS(OS) {1774OS << " {\n";1775}1776~EntryWriter() {1777OS << " },\n";1778}17791780void writeLoc(SourceLocation Loc) {1781FileID FID;1782unsigned Offset;1783std::tie(FID, Offset) = SourceMgr.getDecomposedLoc(Loc);1784assert(FID.isValid());1785SmallString<200> Path =1786StringRef(SourceMgr.getFileEntryRefForID(FID)->getName());1787llvm::sys::fs::make_absolute(Path);1788OS << " \"file\": \"";1789OS.write_escaped(Path.str()) << "\",\n";1790OS << " \"offset\": " << Offset << ",\n";1791}17921793void writeRemove(CharSourceRange Range) {1794assert(Range.isCharRange());1795std::pair<FileID, unsigned> Begin =1796SourceMgr.getDecomposedLoc(Range.getBegin());1797std::pair<FileID, unsigned> End =1798SourceMgr.getDecomposedLoc(Range.getEnd());1799assert(Begin.first == End.first);1800assert(Begin.second <= End.second);1801unsigned Length = End.second - Begin.second;18021803OS << " \"remove\": " << Length << ",\n";1804}18051806void writeText(StringRef Text) {1807OS << " \"text\": \"";1808OS.write_escaped(Text) << "\",\n";1809}1810};18111812void insert(SourceLocation Loc, StringRef Text) override {1813EntryWriter Writer(SourceMgr, OS);1814Writer.writeLoc(Loc);1815Writer.writeText(Text);1816}18171818void replace(CharSourceRange Range, StringRef Text) override {1819EntryWriter Writer(SourceMgr, OS);1820Writer.writeLoc(Range.getBegin());1821Writer.writeRemove(Range);1822Writer.writeText(Text);1823}18241825void remove(CharSourceRange Range) override {1826EntryWriter Writer(SourceMgr, OS);1827Writer.writeLoc(Range.getBegin());1828Writer.writeRemove(Range);1829}1830};18311832} // end anonymous namespace18331834void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {18351836TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();1837if (ASTMigrateActions & FrontendOptions::ObjCMT_MigrateDecls) {1838for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();1839D != DEnd; ++D) {1840FileID FID = PP.getSourceManager().getFileID((*D)->getLocation());1841if (FID.isValid())1842if (FileId.isValid() && FileId != FID) {1843if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)1844AnnotateImplicitBridging(Ctx);1845}18461847if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))1848if (canModify(CDecl))1849migrateObjCContainerDecl(Ctx, CDecl);1850if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(*D)) {1851if (canModify(CatDecl))1852migrateObjCContainerDecl(Ctx, CatDecl);1853}1854else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D)) {1855ObjCProtocolDecls.insert(PDecl->getCanonicalDecl());1856if (canModify(PDecl))1857migrateObjCContainerDecl(Ctx, PDecl);1858}1859else if (const ObjCImplementationDecl *ImpDecl =1860dyn_cast<ObjCImplementationDecl>(*D)) {1861if ((ASTMigrateActions & FrontendOptions::ObjCMT_ProtocolConformance) &&1862canModify(ImpDecl))1863migrateProtocolConformance(Ctx, ImpDecl);1864}1865else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {1866if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros))1867continue;1868if (!canModify(ED))1869continue;1870DeclContext::decl_iterator N = D;1871if (++N != DEnd) {1872const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N);1873if (migrateNSEnumDecl(Ctx, ED, TD) && TD)1874D++;1875}1876else1877migrateNSEnumDecl(Ctx, ED, /*TypedefDecl */nullptr);1878}1879else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*D)) {1880if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros))1881continue;1882if (!canModify(TD))1883continue;1884DeclContext::decl_iterator N = D;1885if (++N == DEnd)1886continue;1887if (const EnumDecl *ED = dyn_cast<EnumDecl>(*N)) {1888if (canModify(ED)) {1889if (++N != DEnd)1890if (const TypedefDecl *TDF = dyn_cast<TypedefDecl>(*N)) {1891// prefer typedef-follows-enum to enum-follows-typedef pattern.1892if (migrateNSEnumDecl(Ctx, ED, TDF)) {1893++D; ++D;1894CacheObjCNSIntegerTypedefed(TD);1895continue;1896}1897}1898if (migrateNSEnumDecl(Ctx, ED, TD)) {1899++D;1900continue;1901}1902}1903}1904CacheObjCNSIntegerTypedefed(TD);1905}1906else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D)) {1907if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&1908canModify(FD))1909migrateCFAnnotation(Ctx, FD);1910}19111912if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) {1913bool CanModify = canModify(CDecl);1914// migrate methods which can have instancetype as their result type.1915if ((ASTMigrateActions & FrontendOptions::ObjCMT_Instancetype) &&1916CanModify)1917migrateAllMethodInstaceType(Ctx, CDecl);1918// annotate methods with CF annotations.1919if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&1920CanModify)1921migrateARCSafeAnnotation(Ctx, CDecl);1922}19231924if (const ObjCImplementationDecl *1925ImplD = dyn_cast<ObjCImplementationDecl>(*D)) {1926if ((ASTMigrateActions & FrontendOptions::ObjCMT_DesignatedInitializer) &&1927canModify(ImplD))1928inferDesignatedInitializers(Ctx, ImplD);1929}1930}1931if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)1932AnnotateImplicitBridging(Ctx);1933}19341935if (IsOutputFile) {1936std::error_code EC;1937llvm::raw_fd_ostream OS(MigrateDir, EC, llvm::sys::fs::OF_None);1938if (EC) {1939DiagnosticsEngine &Diags = Ctx.getDiagnostics();1940Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Error, "%0"))1941<< EC.message();1942return;1943}19441945JSONEditWriter Writer(Ctx.getSourceManager(), OS);1946Editor->applyRewrites(Writer);1947return;1948}19491950Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());1951RewritesReceiver Rec(rewriter);1952Editor->applyRewrites(Rec);19531954for (Rewriter::buffer_iterator1955I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {1956FileID FID = I->first;1957RewriteBuffer &buf = I->second;1958OptionalFileEntryRef file =1959Ctx.getSourceManager().getFileEntryRefForID(FID);1960assert(file);1961SmallString<512> newText;1962llvm::raw_svector_ostream vecOS(newText);1963buf.write(vecOS);1964std::unique_ptr<llvm::MemoryBuffer> memBuf(1965llvm::MemoryBuffer::getMemBufferCopy(newText.str(), file->getName()));1966SmallString<64> filePath(file->getName());1967FileMgr.FixupRelativePath(filePath);1968Remapper.remap(filePath.str(), std::move(memBuf));1969}19701971if (IsOutputFile) {1972Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());1973} else {1974Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());1975}1976}19771978bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {1979CI.getDiagnostics().setIgnoreAllWarnings(true);1980return true;1981}19821983static std::vector<std::string> getAllowListFilenames(StringRef DirPath) {1984using namespace llvm::sys::fs;1985using namespace llvm::sys::path;19861987std::vector<std::string> Filenames;1988if (DirPath.empty() || !is_directory(DirPath))1989return Filenames;19901991std::error_code EC;1992directory_iterator DI = directory_iterator(DirPath, EC);1993directory_iterator DE;1994for (; !EC && DI != DE; DI = DI.increment(EC)) {1995if (is_regular_file(DI->path()))1996Filenames.push_back(std::string(filename(DI->path())));1997}19981999return Filenames;2000}20012002std::unique_ptr<ASTConsumer>2003MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {2004PPConditionalDirectiveRecord *2005PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());2006unsigned ObjCMTAction = CI.getFrontendOpts().ObjCMTAction;2007unsigned ObjCMTOpts = ObjCMTAction;2008// These are companion flags, they do not enable transformations.2009ObjCMTOpts &= ~(FrontendOptions::ObjCMT_AtomicProperty |2010FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty);2011if (ObjCMTOpts == FrontendOptions::ObjCMT_None) {2012// If no specific option was given, enable literals+subscripting transforms2013// by default.2014ObjCMTAction |=2015FrontendOptions::ObjCMT_Literals | FrontendOptions::ObjCMT_Subscripting;2016}2017CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec));2018std::vector<std::string> AllowList =2019getAllowListFilenames(CI.getFrontendOpts().ObjCMTAllowListPath);2020return std::make_unique<ObjCMigrateASTConsumer>(2021CI.getFrontendOpts().OutputFile, ObjCMTAction, Remapper,2022CI.getFileManager(), PPRec, CI.getPreprocessor(),2023/*isOutputFile=*/true, AllowList);2024}20252026namespace {2027struct EditEntry {2028OptionalFileEntryRef File;2029unsigned Offset = 0;2030unsigned RemoveLen = 0;2031std::string Text;2032};2033} // end anonymous namespace20342035namespace llvm {2036template<> struct DenseMapInfo<EditEntry> {2037static inline EditEntry getEmptyKey() {2038EditEntry Entry;2039Entry.Offset = unsigned(-1);2040return Entry;2041}2042static inline EditEntry getTombstoneKey() {2043EditEntry Entry;2044Entry.Offset = unsigned(-2);2045return Entry;2046}2047static unsigned getHashValue(const EditEntry& Val) {2048return (unsigned)llvm::hash_combine(Val.File, Val.Offset, Val.RemoveLen,2049Val.Text);2050}2051static bool isEqual(const EditEntry &LHS, const EditEntry &RHS) {2052return LHS.File == RHS.File &&2053LHS.Offset == RHS.Offset &&2054LHS.RemoveLen == RHS.RemoveLen &&2055LHS.Text == RHS.Text;2056}2057};2058} // end namespace llvm20592060namespace {2061class RemapFileParser {2062FileManager &FileMgr;20632064public:2065RemapFileParser(FileManager &FileMgr) : FileMgr(FileMgr) { }20662067bool parse(StringRef File, SmallVectorImpl<EditEntry> &Entries) {2068using namespace llvm::yaml;20692070llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =2071llvm::MemoryBuffer::getFile(File);2072if (!FileBufOrErr)2073return true;20742075llvm::SourceMgr SM;2076Stream YAMLStream(FileBufOrErr.get()->getMemBufferRef(), SM);2077document_iterator I = YAMLStream.begin();2078if (I == YAMLStream.end())2079return true;2080Node *Root = I->getRoot();2081if (!Root)2082return true;20832084SequenceNode *SeqNode = dyn_cast<SequenceNode>(Root);2085if (!SeqNode)2086return true;20872088for (SequenceNode::iterator2089AI = SeqNode->begin(), AE = SeqNode->end(); AI != AE; ++AI) {2090MappingNode *MapNode = dyn_cast<MappingNode>(&*AI);2091if (!MapNode)2092continue;2093parseEdit(MapNode, Entries);2094}20952096return false;2097}20982099private:2100void parseEdit(llvm::yaml::MappingNode *Node,2101SmallVectorImpl<EditEntry> &Entries) {2102using namespace llvm::yaml;2103EditEntry Entry;2104bool Ignore = false;21052106for (MappingNode::iterator2107KVI = Node->begin(), KVE = Node->end(); KVI != KVE; ++KVI) {2108ScalarNode *KeyString = dyn_cast<ScalarNode>((*KVI).getKey());2109if (!KeyString)2110continue;2111SmallString<10> KeyStorage;2112StringRef Key = KeyString->getValue(KeyStorage);21132114ScalarNode *ValueString = dyn_cast<ScalarNode>((*KVI).getValue());2115if (!ValueString)2116continue;2117SmallString<64> ValueStorage;2118StringRef Val = ValueString->getValue(ValueStorage);21192120if (Key == "file") {2121if (auto File = FileMgr.getOptionalFileRef(Val))2122Entry.File = File;2123else2124Ignore = true;2125} else if (Key == "offset") {2126if (Val.getAsInteger(10, Entry.Offset))2127Ignore = true;2128} else if (Key == "remove") {2129if (Val.getAsInteger(10, Entry.RemoveLen))2130Ignore = true;2131} else if (Key == "text") {2132Entry.Text = std::string(Val);2133}2134}21352136if (!Ignore)2137Entries.push_back(Entry);2138}2139};2140} // end anonymous namespace21412142static bool reportDiag(const Twine &Err, DiagnosticsEngine &Diag) {2143Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))2144<< Err.str();2145return true;2146}21472148static std::string applyEditsToTemp(FileEntryRef FE,2149ArrayRef<EditEntry> Edits,2150FileManager &FileMgr,2151DiagnosticsEngine &Diag) {2152using namespace llvm::sys;21532154SourceManager SM(Diag, FileMgr);2155FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User);2156LangOptions LangOpts;2157edit::EditedSource Editor(SM, LangOpts);2158for (ArrayRef<EditEntry>::iterator2159I = Edits.begin(), E = Edits.end(); I != E; ++I) {2160const EditEntry &Entry = *I;2161assert(Entry.File == FE);2162SourceLocation Loc =2163SM.getLocForStartOfFile(FID).getLocWithOffset(Entry.Offset);2164CharSourceRange Range;2165if (Entry.RemoveLen != 0) {2166Range = CharSourceRange::getCharRange(Loc,2167Loc.getLocWithOffset(Entry.RemoveLen));2168}21692170edit::Commit commit(Editor);2171if (Range.isInvalid()) {2172commit.insert(Loc, Entry.Text);2173} else if (Entry.Text.empty()) {2174commit.remove(Range);2175} else {2176commit.replace(Range, Entry.Text);2177}2178Editor.commit(commit);2179}21802181Rewriter rewriter(SM, LangOpts);2182RewritesReceiver Rec(rewriter);2183Editor.applyRewrites(Rec, /*adjustRemovals=*/false);21842185const RewriteBuffer *Buf = rewriter.getRewriteBufferFor(FID);2186SmallString<512> NewText;2187llvm::raw_svector_ostream OS(NewText);2188Buf->write(OS);21892190SmallString<64> TempPath;2191int FD;2192if (fs::createTemporaryFile(path::filename(FE.getName()),2193path::extension(FE.getName()).drop_front(), FD,2194TempPath)) {2195reportDiag("Could not create file: " + TempPath.str(), Diag);2196return std::string();2197}21982199llvm::raw_fd_ostream TmpOut(FD, /*shouldClose=*/true);2200TmpOut.write(NewText.data(), NewText.size());2201TmpOut.close();22022203return std::string(TempPath);2204}22052206bool arcmt::getFileRemappingsFromFileList(2207std::vector<std::pair<std::string,std::string> > &remap,2208ArrayRef<StringRef> remapFiles,2209DiagnosticConsumer *DiagClient) {2210bool hasErrorOccurred = false;22112212FileSystemOptions FSOpts;2213FileManager FileMgr(FSOpts);2214RemapFileParser Parser(FileMgr);22152216IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());2217IntrusiveRefCntPtr<DiagnosticsEngine> Diags(2218new DiagnosticsEngine(DiagID, new DiagnosticOptions,2219DiagClient, /*ShouldOwnClient=*/false));22202221typedef llvm::DenseMap<FileEntryRef, std::vector<EditEntry> >2222FileEditEntriesTy;2223FileEditEntriesTy FileEditEntries;22242225llvm::DenseSet<EditEntry> EntriesSet;22262227for (ArrayRef<StringRef>::iterator2228I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) {2229SmallVector<EditEntry, 16> Entries;2230if (Parser.parse(*I, Entries))2231continue;22322233for (SmallVectorImpl<EditEntry>::iterator2234EI = Entries.begin(), EE = Entries.end(); EI != EE; ++EI) {2235EditEntry &Entry = *EI;2236if (!Entry.File)2237continue;2238std::pair<llvm::DenseSet<EditEntry>::iterator, bool>2239Insert = EntriesSet.insert(Entry);2240if (!Insert.second)2241continue;22422243FileEditEntries[*Entry.File].push_back(Entry);2244}2245}22462247for (FileEditEntriesTy::iterator2248I = FileEditEntries.begin(), E = FileEditEntries.end(); I != E; ++I) {2249std::string TempFile = applyEditsToTemp(I->first, I->second,2250FileMgr, *Diags);2251if (TempFile.empty()) {2252hasErrorOccurred = true;2253continue;2254}22552256remap.emplace_back(std::string(I->first.getName()), TempFile);2257}22582259return hasErrorOccurred;2260}226122622263