text: //===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===//
  //
  //                     The LLVM Compiler Infrastructure
  //
  // This file is distributed under the University of Illinois Open Source
  // License. See LICENSE.TXT for details.
  //
  //===----------------------------------------------------------------------===//
  
  #include "Transforms.h"
  #include "clang/ARCMigrate/ARCMT.h"
  #include "clang/ARCMigrate/ARCMTActions.h"
  #include "clang/AST/ASTConsumer.h"
  #include "clang/AST/ASTContext.h"
  #include "clang/AST/Attr.h"
  #include "clang/AST/NSAPI.h"
  #include "clang/AST/ParentMap.h"
  #include "clang/AST/RecursiveASTVisitor.h"
  #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
  #include "clang/Basic/FileManager.h"
  #include "clang/Edit/Commit.h"
  #include "clang/Edit/EditedSource.h"
  #include "clang/Edit/EditsReceiver.h"
  #include "clang/Edit/Rewriters.h"
  #include "clang/Frontend/CompilerInstance.h"
  #include "clang/Frontend/MultiplexConsumer.h"
  #include "clang/Lex/PPConditionalDirectiveRecord.h"
  #include "clang/Lex/Preprocessor.h"
  #include "clang/Rewrite/Core/Rewriter.h"
  #include "clang/StaticAnalyzer/Core/RetainSummaryManager.h"
  #include "llvm/ADT/SmallString.h"
  #include "llvm/ADT/StringSet.h"
  #include "llvm/Support/Path.h"
  #include "llvm/Support/SourceMgr.h"
  #include "llvm/Support/YAMLParser.h"
  
  using namespace clang;
  using namespace arcmt;
  using namespace ento;
  
  namespace {
  
  class ObjCMigrateASTConsumer : public ASTConsumer {
    enum CF_BRIDGING_KIND {
      CF_BRIDGING_NONE,
      CF_BRIDGING_ENABLE,
      CF_BRIDGING_MAY_INCLUDE
    };
  
    void migrateDecl(Decl *D);
    void migrateObjCContainerDecl(ASTContext &Ctx, ObjCContainerDecl *D);
    void migrateProtocolConformance(ASTContext &Ctx,
                                    const ObjCImplementationDecl *ImpDecl);
    void CacheObjCNSIntegerTypedefed(const TypedefDecl *TypedefDcl);
    bool migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl,
                       const TypedefDecl *TypedefDcl);
    void migrateAllMethodInstaceType(ASTContext &Ctx, ObjCContainerDecl *CDecl);
    void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl,
                                   ObjCMethodDecl *OM);
    bool migrateProperty(ASTContext &Ctx, ObjCContainerDecl *D, ObjCMethodDecl *OM);
    void migrateNsReturnsInnerPointer(ASTContext &Ctx, ObjCMethodDecl *OM);
    void migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, ObjCPropertyDecl *P);
    void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl,
                              ObjCMethodDecl *OM,
                              ObjCInstanceTypeFamily OIT_Family = OIT_None);
  
    void migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl);
    void AddCFAnnotations(ASTContext &Ctx, const CallEffects &CE,
                          const FunctionDecl *FuncDecl, bool ResultAnnotated);
    void AddCFAnnotations(ASTContext &Ctx, const CallEffects &CE,
                          const ObjCMethodDecl *MethodDecl, bool ResultAnnotated);
  
    void AnnotateImplicitBridging(ASTContext &Ctx);
  
    CF_BRIDGING_KIND migrateAddFunctionAnnotation(ASTContext &Ctx,
                                                  const FunctionDecl *FuncDecl);
  
    void migrateARCSafeAnnotation(ASTContext &Ctx, ObjCContainerDecl *CDecl);
  
    void migrateAddMethodAnnotation(ASTContext &Ctx,
                                    const ObjCMethodDecl *MethodDecl);
  
    void inferDesignatedInitializers(ASTContext &Ctx,
                                     const ObjCImplementationDecl *ImplD);
  
    bool InsertFoundation(ASTContext &Ctx, SourceLocation Loc);
  
  public:
    std::string MigrateDir;
    unsigned ASTMigrateActions;
    FileID FileId;
    const TypedefDecl *NSIntegerTypedefed;
    const TypedefDecl *NSUIntegerTypedefed;
    std::unique_ptr<NSAPI> NSAPIObj;
    std::unique_ptr<edit::EditedSource> Editor;
    FileRemapper &Remapper;
    FileManager &FileMgr;
    const PPConditionalDirectiveRecord *PPRec;
    Preprocessor &PP;
    bool IsOutputFile;
    bool FoundationIncluded;
    llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls;
    llvm::SmallVector<const Decl *, 8> CFFunctionIBCandidates;
    llvm::StringSet<> WhiteListFilenames;
  
    ObjCMigrateASTConsumer(StringRef migrateDir,
                           unsigned astMigrateActions,
                           FileRemapper &remapper,
                           FileManager &fileMgr,
                           const PPConditionalDirectiveRecord *PPRec,
                           Preprocessor &PP,
                           bool isOutputFile,
                           ArrayRef<std::string> WhiteList)
    : MigrateDir(migrateDir),
      ASTMigrateActions(astMigrateActions),
      NSIntegerTypedefed(nullptr), NSUIntegerTypedefed(nullptr),
      Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),
      IsOutputFile(isOutputFile),
      FoundationIncluded(false){
  
      // FIXME: StringSet should have insert(iter, iter) to use here.
      for (const std::string &Val : WhiteList)
        WhiteListFilenames.insert(Val);
    }
  
  protected:
    void Initialize(ASTContext &Context) override {
      NSAPIObj.reset(new NSAPI(Context));
      Editor.reset(new edit::EditedSource(Context.getSourceManager(),
                                          Context.getLangOpts(),
                                          PPRec));
    }
  
    bool HandleTopLevelDecl(DeclGroupRef DG) override {
      for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
        migrateDecl(*I);
      return true;
    }
    void HandleInterestingDecl(DeclGroupRef DG) override {
      // Ignore decls from the PCH.
    }
    void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override {
      ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
    }
  
    void HandleTranslationUnit(ASTContext &Ctx) override;
  
    bool canModifyFile(StringRef Path) {
      if (WhiteListFilenames.empty())
        return true;
      return WhiteListFilenames.find(llvm::sys::path::filename(Path))
          != WhiteListFilenames.end();
    }
    bool canModifyFile(const FileEntry *FE) {
      if (!FE)
        return false;
      return canModifyFile(FE->getName());
    }
    bool canModifyFile(FileID FID) {
      if (FID.isInvalid())
        return false;
      return canModifyFile(PP.getSourceManager().getFileEntryForID(FID));
    }
  
    bool canModify(const Decl *D) {
      if (!D)
        return false;
      if (const ObjCCategoryImplDecl *CatImpl = dyn_cast<ObjCCategoryImplDecl>(D))
        return canModify(CatImpl->getCategoryDecl());
      if (const ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(D))
        return canModify(Impl->getClassInterface());
      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
        return canModify(cast<Decl>(MD->getDeclContext()));
  
      FileID FID = PP.getSourceManager().getFileID(D->getLocation());
      return canModifyFile(FID);
    }
  };
  
  } // end anonymous namespace
  
  ObjCMigrateAction::ObjCMigrateAction(
                                    std::unique_ptr<FrontendAction> WrappedAction,
                                       StringRef migrateDir,
                                       unsigned migrateAction)
    : WrapperFrontendAction(std::move(WrappedAction)), MigrateDir(migrateDir),
      ObjCMigAction(migrateAction),
      CompInst(nullptr) {
    if (MigrateDir.empty())
      MigrateDir = "."; // user current directory if none is given.
  }
  
  std::unique_ptr<ASTConsumer>
  ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
    PPConditionalDirectiveRecord *
      PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
    CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec));
    std::vector<std::unique_ptr<ASTConsumer>> Consumers;
    Consumers.push_back(WrapperFrontendAction::CreateASTConsumer(CI, InFile));
    Consumers.push_back(llvm::make_unique<ObjCMigrateASTConsumer>(
        MigrateDir, ObjCMigAction, Remapper, CompInst->getFileManager(), PPRec,
        CompInst->getPreprocessor(), false, None));
    return llvm::make_unique<MultiplexConsumer>(std::move(Consumers));
  }
  
  bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
    Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
                          /*ignoreIfFilesChanges=*/true);
    CompInst = &CI;
    CI.getDiagnostics().setIgnoreAllWarnings(true);
    return true;
  }
  
  namespace {
    // FIXME. This duplicates one in RewriteObjCFoundationAPI.cpp
    bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
      const Expr* Expr = FullExpr->IgnoreImpCasts();
      return !(isa<ArraySubscriptExpr>(Expr) || isa<CallExpr>(Expr) ||
               isa<DeclRefExpr>(Expr) || isa<CXXNamedCastExpr>(Expr) ||
               isa<CXXConstructExpr>(Expr) || isa<CXXThisExpr>(Expr) ||
               isa<CXXTypeidExpr>(Expr) ||
               isa<CXXUnresolvedConstructExpr>(Expr) ||
               isa<ObjCMessageExpr>(Expr) || isa<ObjCPropertyRefExpr>(Expr) ||
               isa<ObjCProtocolExpr>(Expr) || isa<MemberExpr>(Expr) ||
               isa<ObjCIvarRefExpr>(Expr) || isa<ParenExpr>(FullExpr) ||
               isa<ParenListExpr>(Expr) || isa<SizeOfPackExpr>(Expr));
    }
  
    /// - Rewrite message expression for Objective-C setter and getters into
    /// property-dot syntax.
    bool rewriteToPropertyDotSyntax(const ObjCMessageExpr *Msg,
                                    Preprocessor &PP,
                                    const NSAPI &NS, edit::Commit &commit,
                                    const ParentMap *PMap) {
      if (!Msg || Msg->isImplicit() ||
          (Msg->getReceiverKind() != ObjCMessageExpr::Instance &&
           Msg->getReceiverKind() != ObjCMessageExpr::SuperInstance))
        return false;
      if (const Expr *Receiver = Msg->getInstanceReceiver())
        if (Receiver->getType()->isObjCBuiltinType())
          return false;
  
      const ObjCMethodDecl *Method = Msg->getMethodDecl();
      if (!Method)
        return false;
      if (!Method->isPropertyAccessor())
        return false;
  
      const ObjCPropertyDecl *Prop = Method->findPropertyDecl();
      if (!Prop)
        return false;
  
      SourceRange MsgRange = Msg->getSourceRange();
      bool ReceiverIsSuper =
        (Msg->getReceiverKind() == ObjCMessageExpr::SuperInstance);
      // for 'super' receiver is nullptr.
      const Expr *receiver = Msg->getInstanceReceiver();
      bool NeedsParen =
        ReceiverIsSuper ? false : subscriptOperatorNeedsParens(receiver);
      bool IsGetter = (Msg->getNumArgs() == 0);
      if (IsGetter) {
        // Find space location range between receiver expression and getter method.
        SourceLocation BegLoc =
            ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getEndLoc();
        BegLoc = PP.getLocForEndOfToken(BegLoc);
        SourceLocation EndLoc = Msg->getSelectorLoc(0);
        SourceRange SpaceRange(BegLoc, EndLoc);
        std::string PropertyDotString;
        // rewrite getter method expression into: receiver.property or
        // (receiver).property
        if (NeedsParen) {
          commit.insertBefore(receiver->getBeginLoc(), "(");
          PropertyDotString = ").";
        }
        else
          PropertyDotString = ".";
        PropertyDotString += Prop->getName();
        commit.replace(SpaceRange, PropertyDotString);
  
        // remove '[' ']'
        commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), "");
        commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), "");
      } else {
        if (NeedsParen)
          commit.insertWrap("(", receiver->getSourceRange(), ")");
        std::string PropertyDotString = ".";
        PropertyDotString += Prop->getName();
        PropertyDotString += " =";
        const Expr*const* Args = Msg->getArgs();
        const Expr *RHS = Args[0];
        if (!RHS)
          return false;
        SourceLocation BegLoc =
            ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getEndLoc();
        BegLoc = PP.getLocForEndOfToken(BegLoc);
        SourceLocation EndLoc = RHS->getBeginLoc();
        EndLoc = EndLoc.getLocWithOffset(-1);
        const char *colon = PP.getSourceManager().getCharacterData(EndLoc);
        // Add a space after '=' if there is no space between RHS and '='
        if (colon && colon[0] == ':')
          PropertyDotString += " ";
        SourceRange Range(BegLoc, EndLoc);
        commit.replace(Range, PropertyDotString);
        // remove '[' ']'
        commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), "");
        commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), "");
      }
      return true;
    }
  
  class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
    ObjCMigrateASTConsumer &Consumer;
    ParentMap &PMap;
  
  public:
    ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
      : Consumer(consumer), PMap(PMap) { }
  
    bool shouldVisitTemplateInstantiations() const { return false; }
    bool shouldWalkTypesOfTypeLocs() const { return false; }
  
    bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
      if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Literals) {
        edit::Commit commit(*Consumer.Editor);
        edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
        Consumer.Editor->commit(commit);
      }
  
      if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Subscripting) {
        edit::Commit commit(*Consumer.Editor);
        edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
        Consumer.Editor->commit(commit);
      }
  
      if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_PropertyDotSyntax) {
        edit::Commit commit(*Consumer.Editor);
        rewriteToPropertyDotSyntax(E, Consumer.PP, *Consumer.NSAPIObj,
                                   commit, &PMap);
        Consumer.Editor->commit(commit);
      }
  
      return true;
    }
  
    bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
      // Do depth first; we want to rewrite the subexpressions first so that if
      // we have to move expressions we will move them already rewritten.
      for (Stmt *SubStmt : E->children())
        if (!TraverseStmt(SubStmt))
          return false;
  
      return WalkUpFromObjCMessageExpr(E);
    }
  };
  
  class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
    ObjCMigrateASTConsumer &Consumer;
    std::unique_ptr<ParentMap> PMap;
  
  public:
    BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
  
    bool shouldVisitTemplateInstantiations() const { return false; }
    bool shouldWalkTypesOfTypeLocs() const { return false; }
  
    bool TraverseStmt(Stmt *S) {
      PMap.reset(new ParentMap(S));
      ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
      return true;
    }
  };
  } // end anonymous namespace
  
  void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
    if (!D)
      return;
    if (isa<ObjCMethodDecl>(D))
      return; // Wait for the ObjC container declaration.
  
    BodyMigrator(*this).TraverseDecl(D);
  }
  
  static void append_attr(std::string &PropertyString, const char *attr,
                          bool &LParenAdded) {
    if (!LParenAdded) {
      PropertyString += "(";
      LParenAdded = true;
    }
    else
      PropertyString += ", ";
    PropertyString += attr;
  }
  
  static
  void MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString,
                                                 const std::string& TypeString,
                                                 const char *name) {
    const char *argPtr = TypeString.c_str();
    int paren = 0;
    while (*argPtr) {
      switch (*argPtr) {
        case '(':
          PropertyString += *argPtr;
          paren++;
          break;
        case ')':
          PropertyString += *argPtr;
          paren--;
          break;
        case '^':
        case '*':
          PropertyString += (*argPtr);
          if (paren == 1) {
            PropertyString += name;
            name = "";
          }
          break;
        default:
          PropertyString += *argPtr;
          break;
      }
      argPtr++;
    }
  }
  
  static const char *PropertyMemoryAttribute(ASTContext &Context, QualType ArgType) {
    Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();
    bool RetainableObject = ArgType->isObjCRetainableType();
    if (RetainableObject &&
        (propertyLifetime == Qualifiers::OCL_Strong
         || propertyLifetime == Qualifiers::OCL_None)) {
      if (const ObjCObjectPointerType *ObjPtrTy =
          ArgType->getAs<ObjCObjectPointerType>()) {
        ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
        if (IDecl &&
            IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying")))
          return "copy";
        else
          return "strong";
      }
      else if (ArgType->isBlockPointerType())
        return "copy";
    } else if (propertyLifetime == Qualifiers::OCL_Weak)
      // TODO. More precise determination of 'weak' attribute requires
      // looking into setter's implementation for backing weak ivar.
      return "weak";
    else if (RetainableObject)
      return ArgType->isBlockPointerType() ? "copy" : "strong";
    return nullptr;
  }
  
  static void rewriteToObjCProperty(const ObjCMethodDecl *Getter,
                                    const ObjCMethodDecl *Setter,
                                    const NSAPI &NS, edit::Commit &commit,
                                    unsigned LengthOfPrefix,
                                    bool Atomic, bool UseNsIosOnlyMacro,
                                    bool AvailabilityArgsMatch) {
    ASTContext &Context = NS.getASTContext();
    bool LParenAdded = false;
    std::string PropertyString = "@property ";
    if (UseNsIosOnlyMacro && NS.isMacroDefined("NS_NONATOMIC_IOSONLY")) {
      PropertyString += "(NS_NONATOMIC_IOSONLY";
      LParenAdded = true;
    } else if (!Atomic) {
      PropertyString += "(nonatomic";
      LParenAdded = true;
    }
  
    std::string PropertyNameString = Getter->getNameAsString();
    StringRef PropertyName(PropertyNameString);
    if (LengthOfPrefix > 0) {
      if (!LParenAdded) {
        PropertyString += "(getter=";
        LParenAdded = true;
      }
      else
        PropertyString += ", getter=";
      PropertyString += PropertyNameString;
    }
    // Property with no setter may be suggested as a 'readonly' property.
    if (!Setter)
      append_attr(PropertyString, "readonly", LParenAdded);
  
  
    // Short circuit 'delegate' properties that contain the name "delegate" or
    // "dataSource", or have exact name "target" to have 'assign' attribute.
    if (PropertyName.equals("target") ||
        (PropertyName.find("delegate") != StringRef::npos) ||
        (PropertyName.find("dataSource") != StringRef::npos)) {
      QualType QT = Getter->getReturnType();
      if (!QT->isRealType())
        append_attr(PropertyString, "assign", LParenAdded);
    } else if (!Setter) {
      QualType ResType = Context.getCanonicalType(Getter->getReturnType());
      if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ResType))
        append_attr(PropertyString, MemoryManagementAttr, LParenAdded);
    } else {
      const ParmVarDecl *argDecl = *Setter->param_begin();
      QualType ArgType = Context.getCanonicalType(argDecl->getType());
      if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ArgType))
        append_attr(PropertyString, MemoryManagementAttr, LParenAdded);
    }
    if (LParenAdded)
      PropertyString += ')';
    QualType RT = Getter->getReturnType();
    if (!isa<TypedefType>(RT)) {
      // strip off any ARC lifetime qualifier.
      QualType CanResultTy = Context.getCanonicalType(RT);
      if (CanResultTy.getQualifiers().hasObjCLifetime()) {
        Qualifiers Qs = CanResultTy.getQualifiers();
        Qs.removeObjCLifetime();
        RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs);
      }
    }
    PropertyString += " ";
    PrintingPolicy SubPolicy(Context.getPrintingPolicy());
    SubPolicy.SuppressStrongLifetime = true;
    SubPolicy.SuppressLifetimeQualifiers = true;
    std::string TypeString = RT.getAsString(SubPolicy);
    if (LengthOfPrefix > 0) {
      // property name must strip off "is" and lower case the first character
      // after that; e.g. isContinuous will become continuous.
      StringRef PropertyNameStringRef(PropertyNameString);
      PropertyNameStringRef = PropertyNameStringRef.drop_front(LengthOfPrefix);
      PropertyNameString = PropertyNameStringRef;
      bool NoLowering = (isUppercase(PropertyNameString[0]) &&
                         PropertyNameString.size() > 1 &&
                         isUppercase(PropertyNameString[1]));
      if (!NoLowering)
        PropertyNameString[0] = toLowercase(PropertyNameString[0]);
    }
    if (RT->isBlockPointerType() || RT->isFunctionPointerType())
      MigrateBlockOrFunctionPointerTypeVariable(PropertyString,
                                                TypeString,
                                                PropertyNameString.c_str());
    else {
      char LastChar = TypeString[TypeString.size()-1];
      PropertyString += TypeString;
      if (LastChar != '*')
        PropertyString += ' ';
      PropertyString += PropertyNameString;
    }
    SourceLocation StartGetterSelectorLoc = Getter->getSelectorStartLoc();
    Selector GetterSelector = Getter->getSelector();
  
    SourceLocation EndGetterSelectorLoc =
      StartGetterSelectorLoc.getLocWithOffset(GetterSelector.getNameForSlot(0).size());
    commit.replace(CharSourceRange::getCharRange(Getter->getBeginLoc(),
                                                 EndGetterSelectorLoc),
                   PropertyString);
    if (Setter && AvailabilityArgsMatch) {
      SourceLocation EndLoc = Setter->getDeclaratorEndLoc();
      // Get location past ';'
      EndLoc = EndLoc.getLocWithOffset(1);
      SourceLocation BeginOfSetterDclLoc = Setter->getBeginLoc();
      // FIXME. This assumes that setter decl; is immediately preceded by eoln.
      // It is trying to remove the setter method decl. line entirely.
      BeginOfSetterDclLoc = BeginOfSetterDclLoc.getLocWithOffset(-1);
      commit.remove(SourceRange(BeginOfSetterDclLoc, EndLoc));
    }
  }
  
  static bool IsCategoryNameWithDeprecatedSuffix(ObjCContainerDecl *D) {
    if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(D)) {
      StringRef Name = CatDecl->getName();
      return Name.endswith("Deprecated");
    }
    return false;
  }
  
  void ObjCMigrateASTConsumer::migrateObjCContainerDecl(ASTContext &Ctx,
                                                        ObjCContainerDecl *D) {
    if (D->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(D))
      return;
  
    for (auto *Method : D->methods()) {
      if (Method->isDeprecated())
        continue;
      bool PropertyInferred = migrateProperty(Ctx, D, Method);
      // If a property is inferred, do not attempt to attach NS_RETURNS_INNER_POINTER to
      // the getter method as it ends up on the property itself which we don't want
      // to do unless -objcmt-returns-innerpointer-property  option is on.
      if (!PropertyInferred ||
          (ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty))
        if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
          migrateNsReturnsInnerPointer(Ctx, Method);
    }
    if (!(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty))
      return;
  
    for (auto *Prop : D->instance_properties()) {
      if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
          !Prop->isDeprecated())
        migratePropertyNsReturnsInnerPointer(Ctx, Prop);
    }
  }
  
  static bool
  ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
                                        const ObjCImplementationDecl *ImpDecl,
                                         const ObjCInterfaceDecl *IDecl,
                                        ObjCProtocolDecl *Protocol) {
    // In auto-synthesis, protocol properties are not synthesized. So,
    // a conforming protocol must have its required properties declared
    // in class interface.
    bool HasAtleastOneRequiredProperty = false;
    if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition())
      for (const auto *Property : PDecl->instance_properties()) {
        if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional)
          continue;
        HasAtleastOneRequiredProperty = true;
        DeclContext::lookup_result R = IDecl->lookup(Property->getDeclName());
        if (R.size() == 0) {
          // Relax the rule and look into class's implementation for a synthesize
          // or dynamic declaration. Class is implementing a property coming from
          // another protocol. This still makes the target protocol as conforming.
          if (!ImpDecl->FindPropertyImplDecl(
                                    Property->getDeclName().getAsIdentifierInfo(),
                                    Property->getQueryKind()))
            return false;
        }
        else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) {
            if ((ClassProperty->getPropertyAttributes()
                != Property->getPropertyAttributes()) ||
                !Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
              return false;
        }
        else
          return false;
      }
  
    // At this point, all required properties in this protocol conform to those
    // declared in the class.
    // Check that class implements the required methods of the protocol too.
    bool HasAtleastOneRequiredMethod = false;
    if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) {
      if (PDecl->meth_begin() == PDecl->meth_end())
        return HasAtleastOneRequiredProperty;
      for (const auto *MD : PDecl->methods()) {
        if (MD->isImplicit())
          continue;
        if (MD->getImplementationControl() == ObjCMethodDecl::Optional)
          continue;
        DeclContext::lookup_result R = ImpDecl->lookup(MD->getDeclName());
        if (R.size() == 0)
          return false;
        bool match = false;
        HasAtleastOneRequiredMethod = true;
        for (unsigned I = 0, N = R.size(); I != N; ++I)
          if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0]))
            if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {
              match = true;
              break;
            }
        if (!match)
          return false;
      }
    }
    return HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod;
  }
  
  static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl,
                      llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols,
                      const NSAPI &NS, edit::Commit &commit) {
    const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols();
    std::string ClassString;
    SourceLocation EndLoc =
    IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation();
  
    if (Protocols.empty()) {
      ClassString = '<';
      for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
        ClassString += ConformingProtocols[i]->getNameAsString();
        if (i != (e-1))
          ClassString += ", ";
      }
      ClassString += "> ";
    }
    else {
      ClassString = ", ";
      for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
        ClassString += ConformingProtocols[i]->getNameAsString();
        if (i != (e-1))
          ClassString += ", ";
      }
      ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1;
      EndLoc = *PL;
    }
  
    commit.insertAfterToken(EndLoc, ClassString);
    return true;
  }
  
  static StringRef GetUnsignedName(StringRef NSIntegerName) {
    StringRef UnsignedName = llvm::StringSwitch<StringRef>(NSIntegerName)
      .Case("int8_t", "uint8_t")
      .Case("int16_t", "uint16_t")
      .Case("int32_t", "uint32_t")
      .Case("NSInteger", "NSUInteger")
      .Case("int64_t", "uint64_t")
      .Default(NSIntegerName);
    return UnsignedName;
  }
  
  static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,
                                  const TypedefDecl *TypedefDcl,
                                  const NSAPI &NS, edit::Commit &commit,
                                  StringRef NSIntegerName,
                                  bool NSOptions) {
    std::string ClassString;
    if (NSOptions) {
      ClassString = "typedef NS_OPTIONS(";
      ClassString += GetUnsignedName(NSIntegerName);
    }
    else {
      ClassString = "typedef NS_ENUM(";
      ClassString += NSIntegerName;
    }
    ClassString += ", ";
  
    ClassString += TypedefDcl->getIdentifier()->getName();
    ClassString += ')';
    SourceRange R(EnumDcl->getBeginLoc(), EnumDcl->getBeginLoc());
    commit.replace(R, ClassString);
    SourceLocation EndOfEnumDclLoc = EnumDcl->getEndLoc();
    EndOfEnumDclLoc = trans::findSemiAfterLocation(EndOfEnumDclLoc,
                                                   NS.getASTContext(), /*IsDecl*/true);
    if (EndOfEnumDclLoc.isValid()) {
      SourceRange EnumDclRange(EnumDcl->getBeginLoc(), EndOfEnumDclLoc);
      commit.insertFromRange(TypedefDcl->getBeginLoc(), EnumDclRange);
    }
    else
      return false;
  
    SourceLocation EndTypedefDclLoc = TypedefDcl->getEndLoc();
    EndTypedefDclLoc = trans::findSemiAfterLocation(EndTypedefDclLoc,
                                                   NS.getASTContext(), /*IsDecl*/true);
    if (EndTypedefDclLoc.isValid()) {
      SourceRange TDRange(TypedefDcl->getBeginLoc(), EndTypedefDclLoc);
      commit.remove(TDRange);
    }
    else
      return false;
  
    EndOfEnumDclLoc =
        trans::findLocationAfterSemi(EnumDcl->getEndLoc(), NS.getASTContext(),
                                     /*IsDecl*/ true);
    if (EndOfEnumDclLoc.isValid()) {
      SourceLocation BeginOfEnumDclLoc = EnumDcl->getBeginLoc();
      // FIXME. This assumes that enum decl; is immediately preceded by eoln.
      // It is trying to remove the enum decl. lines entirely.
      BeginOfEnumDclLoc = BeginOfEnumDclLoc.getLocWithOffset(-1);
      commit.remove(SourceRange(BeginOfEnumDclLoc, EndOfEnumDclLoc));
      return true;
    }
    return false;
  }
  
  static void rewriteToNSMacroDecl(ASTContext &Ctx,
                                   const EnumDecl *EnumDcl,
                                  const TypedefDecl *TypedefDcl,
                                  const NSAPI &NS, edit::Commit &commit,
                                   bool IsNSIntegerType) {
    QualType DesignatedEnumType = EnumDcl->getIntegerType();
    assert(!DesignatedEnumType.isNull()
           && "rewriteToNSMacroDecl - underlying enum type is null");
  
    PrintingPolicy Policy(Ctx.getPrintingPolicy());
    std::string TypeString = DesignatedEnumType.getAsString(Policy);
    std::string ClassString = IsNSIntegerType ? "NS_ENUM(" : "NS_OPTIONS(";
    ClassString += TypeString;
    ClassString += ", ";
  
    ClassString += TypedefDcl->getIdentifier()->getName();
    ClassString += ") ";
    SourceLocation EndLoc = EnumDcl->getBraceRange().getBegin();
    if (EndLoc.isInvalid())
      return;
    CharSourceRange R =
        CharSourceRange::getCharRange(EnumDcl->getBeginLoc(), EndLoc);
    commit.replace(R, ClassString);
    // This is to remove spaces between '}' and typedef name.
    SourceLocation StartTypedefLoc = EnumDcl->getEndLoc();
    StartTypedefLoc = StartTypedefLoc.getLocWithOffset(+1);
    SourceLocation EndTypedefLoc = TypedefDcl->getEndLoc();
  
    commit.remove(SourceRange(StartTypedefLoc, EndTypedefLoc));
  }
  
  static bool UseNSOptionsMacro(Preprocessor &PP, ASTContext &Ctx,
                                const EnumDecl *EnumDcl) {
    bool PowerOfTwo = true;
    bool AllHexdecimalEnumerator = true;
    uint64_t MaxPowerOfTwoVal = 0;
    for (auto Enumerator : EnumDcl->enumerators()) {
      const Expr *InitExpr = Enumerator->getInitExpr();
      if (!InitExpr) {
        PowerOfTwo = false;
        AllHexdecimalEnumerator = false;
        continue;
      }
      InitExpr = InitExpr->IgnoreParenCasts();
      if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
        if (BO->isShiftOp() || BO->isBitwiseOp())
          return true;
  
      uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
      if (PowerOfTwo && EnumVal) {
        if (!llvm::isPowerOf2_64(EnumVal))
          PowerOfTwo = false;
        else if (EnumVal > MaxPowerOfTwoVal)
          MaxPowerOfTwoVal = EnumVal;
      }
      if (AllHexdecimalEnumerator && EnumVal) {
        bool FoundHexdecimalEnumerator = false;
        SourceLocation EndLoc = Enumerator->getEndLoc();
        Token Tok;
        if (!PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true))
          if (Tok.isLiteral() && Tok.getLength() > 2) {
            if (const char *StringLit = Tok.getLiteralData())
              FoundHexdecimalEnumerator =
                (StringLit[0] == '0' && (toLowercase(StringLit[1]) == 'x'));
          }
        if (!FoundHexdecimalEnumerator)
          AllHexdecimalEnumerator = false;
      }
    }
    return AllHexdecimalEnumerator || (PowerOfTwo && (MaxPowerOfTwoVal > 2));
  }
  
  void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,
                                              const ObjCImplementationDecl *ImpDecl) {
    const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();
    if (!IDecl || ObjCProtocolDecls.empty() || IDecl->isDeprecated())
      return;
    // Find all implicit conforming protocols for this class
    // and make them explicit.
    llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols;
    Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols);
    llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols;
  
    for (ObjCProtocolDecl *ProtDecl : ObjCProtocolDecls)
      if (!ExplicitProtocols.count(ProtDecl))
        PotentialImplicitProtocols.push_back(ProtDecl);
  
    if (PotentialImplicitProtocols.empty())
      return;
  
    // go through list of non-optional methods and properties in each protocol
    // in the PotentialImplicitProtocols list. If class implements every one of the
    // methods and properties, then this class conforms to this protocol.
    llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols;
    for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)
      if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl,
                                                PotentialImplicitProtocols[i]))
        ConformingProtocols.push_back(PotentialImplicitProtocols[i]);
  
    if (ConformingProtocols.empty())
      return;
  
    // Further reduce number of conforming protocols. If protocol P1 is in the list
    // protocol P2 (P2<P1>), No need to include P1.
    llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols;
    for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
      bool DropIt = false;
      ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i];
      for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {
        ObjCProtocolDecl *PDecl = ConformingProtocols[i1];
        if (PDecl == TargetPDecl)
          continue;
        if (PDecl->lookupProtocolNamed(
              TargetPDecl->getDeclName().getAsIdentifierInfo())) {
          DropIt = true;
          break;
        }
      }
      if (!DropIt)
        MinimalConformingProtocols.push_back(TargetPDecl);
    }
    if (MinimalConformingProtocols.empty())
      return;
    edit::Commit commit(*Editor);
    rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols,
                               *NSAPIObj, commit);
    Editor->commit(commit);
  }
  
  void ObjCMigrateASTConsumer::CacheObjCNSIntegerTypedefed(
                                            const TypedefDecl *TypedefDcl) {
  
    QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
    if (NSAPIObj->isObjCNSIntegerType(qt))
      NSIntegerTypedefed = TypedefDcl;
    else if (NSAPIObj->isObjCNSUIntegerType(qt))
      NSUIntegerTypedefed = TypedefDcl;
  }
  
  bool ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx,
                                             const EnumDecl *EnumDcl,
                                             const TypedefDecl *TypedefDcl) {
    if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() ||
        EnumDcl->isDeprecated())
      return false;
    if (!TypedefDcl) {
      if (NSIntegerTypedefed) {
        TypedefDcl = NSIntegerTypedefed;
        NSIntegerTypedefed = nullptr;
      }
      else if (NSUIntegerTypedefed) {
        TypedefDcl = NSUIntegerTypedefed;
        NSUIntegerTypedefed = nullptr;
      }
      else
        return false;
      FileID FileIdOfTypedefDcl =
        PP.getSourceManager().getFileID(TypedefDcl->getLocation());
      FileID FileIdOfEnumDcl =
        PP.getSourceManager().getFileID(EnumDcl->getLocation());
      if (FileIdOfTypedefDcl != FileIdOfEnumDcl)
        return false;
    }
    if (TypedefDcl->isDeprecated())
      return false;
  
    QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
    StringRef NSIntegerName = NSAPIObj->GetNSIntegralKind(qt);
  
    if (NSIntegerName.empty()) {
      // Also check for typedef enum {...} TD;
      if (const EnumType *EnumTy = qt->getAs<EnumType>()) {
        if (EnumTy->getDecl() == EnumDcl) {
          bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl);
          if (!InsertFoundation(Ctx, TypedefDcl->getBeginLoc()))
            return false;
          edit::Commit commit(*Editor);
          rewriteToNSMacroDecl(Ctx, EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);
          Editor->commit(commit);
          return true;
        }
      }
      return false;
    }
  
    // We may still use NS_OPTIONS based on what we find in the enumertor list.
    bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl);
    if (!InsertFoundation(Ctx, TypedefDcl->getBeginLoc()))
      return false;
    edit::Commit commit(*Editor);
    bool Res = rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj,
                                   commit, NSIntegerName, NSOptions);
    Editor->commit(commit);
    return Res;
  }
  
  static void ReplaceWithInstancetype(ASTContext &Ctx,
                                      const ObjCMigrateASTConsumer &ASTC,
                                      ObjCMethodDecl *OM) {
    if (OM->getReturnType() == Ctx.getObjCInstanceType())
      return; // already has instancetype.
  
    SourceRange R;
    std::string ClassString;
    if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) {
      TypeLoc TL = TSInfo->getTypeLoc();
      R = SourceRange(TL.getBeginLoc(), TL.getEndLoc());
      ClassString = "instancetype";
    }
    else {
      R = SourceRange(OM->getBeginLoc(), OM->getBeginLoc());
      ClassString = OM->isInstanceMethod() ? '-' : '+';
      ClassString += " (instancetype)";
    }
    edit::Commit commit(*ASTC.Editor);
    commit.replace(R, ClassString);
    ASTC.Editor->commit(commit);
  }
  
  static void ReplaceWithClasstype(const ObjCMigrateASTConsumer &ASTC,
                                      ObjCMethodDecl *OM) {
    ObjCInterfaceDecl *IDecl = OM->getClassInterface();
    SourceRange R;
    std::string ClassString;
    if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) {
      TypeLoc TL = TSInfo->getTypeLoc();
      R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); {
        ClassString  = IDecl->getName();
        ClassString += "*";
      }
    }
    else {
      R = SourceRange(OM->getBeginLoc(), OM->getBeginLoc());
      ClassString = "+ (";
      ClassString += IDecl->getName(); ClassString += "*)";
    }
    edit::Commit commit(*ASTC.Editor);
    commit.replace(R, ClassString);
    ASTC.Editor->commit(commit);
  }
  
  void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx,
                                                         ObjCContainerDecl *CDecl,
                                                         ObjCMethodDecl *OM) {
    ObjCInstanceTypeFamily OIT_Family =
      Selector::getInstTypeMethodFamily(OM->getSelector());
  
    std::string ClassName;
    switch (OIT_Family) {
      case OIT_None:
        migrateFactoryMethod(Ctx, CDecl, OM);
        return;
      case OIT_Array:
        ClassName = "NSArray";
        break;
      case OIT_Dictionary:
        ClassName = "NSDictionary";
        break;
      case OIT_Singleton:
        migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton);
        return;
      case OIT_Init:
        if (OM->getReturnType()->isObjCIdType())
          ReplaceWithInstancetype(Ctx, *this, OM);
        return;
      case OIT_ReturnsSelf:
        migrateFactoryMethod(Ctx, CDecl, OM, OIT_ReturnsSelf);
        return;
    }
    if (!OM->getReturnType()->isObjCIdType())
      return;
  
    ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
    if (!IDecl) {
      if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
        IDecl = CatDecl->getClassInterface();
      else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
        IDecl = ImpDecl->getClassInterface();
    }
    if (!IDecl ||
        !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) {
      migrateFactoryMethod(Ctx, CDecl, OM);
      return;
    }
    ReplaceWithInstancetype(Ctx, *this, OM);
  }
  
  static bool TypeIsInnerPointer(QualType T) {
    if (!T->isAnyPointerType())
      return false;
    if (T->isObjCObjectPointerType() || T->isObjCBuiltinType() ||
        T->isBlockPointerType() || T->isFunctionPointerType() ||
        ento::coreFoundation::isCFObjectRef(T))
      return false;
    // Also, typedef-of-pointer-to-incomplete-struct is something that we assume
    // is not an innter pointer type.
    QualType OrigT = T;
    while (const TypedefType *TD = dyn_cast<TypedefType>(T.getTypePtr()))
      T = TD->getDecl()->getUnderlyingType();
    if (OrigT == T || !T->isPointerType())
      return true;
    const PointerType* PT = T->getAs<PointerType>();
    QualType UPointeeT = PT->getPointeeType().getUnqualifiedType();
    if (UPointeeT->isRecordType()) {
      const RecordType *RecordTy = UPointeeT->getAs<RecordType>();
      if (!RecordTy->getDecl()->isCompleteDefinition())
        return false;
    }
    return true;
  }
  
  /// Check whether the two versions match.
  static bool versionsMatch(const VersionTuple &X, const VersionTuple &Y) {
    return (X == Y);
  }
  
  /// AvailabilityAttrsMatch - This routine checks that if comparing two
  /// availability attributes, all their components match. It returns
  /// true, if not dealing with availability or when all components of
  /// availability attributes match. This routine is only called when
  /// the attributes are of the same kind.
  static bool AvailabilityAttrsMatch(Attr *At1, Attr *At2) {
    const AvailabilityAttr *AA1 = dyn_cast<AvailabilityAttr>(At1);
    if (!AA1)
      return true;
    const AvailabilityAttr *AA2 = dyn_cast<AvailabilityAttr>(At2);
  
    VersionTuple Introduced1 = AA1->getIntroduced();
    VersionTuple Deprecated1 = AA1->getDeprecated();
    VersionTuple Obsoleted1 = AA1->getObsoleted();
    bool IsUnavailable1 = AA1->getUnavailable();
    VersionTuple Introduced2 = AA2->getIntroduced();
    VersionTuple Deprecated2 = AA2->getDeprecated();
    VersionTuple Obsoleted2 = AA2->getObsoleted();
    bool IsUnavailable2 = AA2->getUnavailable();
    return (versionsMatch(Introduced1, Introduced2) &&
            versionsMatch(Deprecated1, Deprecated2) &&
            versionsMatch(Obsoleted1, Obsoleted2) &&
            IsUnavailable1 == IsUnavailable2);
  }
  
  static bool MatchTwoAttributeLists(const AttrVec &Attrs1, const AttrVec &Attrs2,
                                     bool &AvailabilityArgsMatch) {
    // This list is very small, so this need not be optimized.
    for (unsigned i = 0, e = Attrs1.size(); i != e; i++) {
      bool match = false;
      for (unsigned j = 0, f = Attrs2.size(); j != f; j++) {
        // Matching attribute kind only. Except for Availability attributes,
        // we are not getting into details of the attributes. For all practical purposes
        // this is sufficient.
        if (Attrs1[i]->getKind() == Attrs2[j]->getKind()) {
          if (AvailabilityArgsMatch)
            AvailabilityArgsMatch = AvailabilityAttrsMatch(Attrs1[i], Attrs2[j]);
          match = true;
          break;
        }
      }
      if (!match)
        return false;
    }
    return true;
  }
  
  /// AttributesMatch - This routine checks list of attributes for two
  /// decls. It returns false, if there is a mismatch in kind of
  /// attributes seen in the decls. It returns true if the two decls
  /// have list of same kind of attributes. Furthermore, when there
  /// are availability attributes in the two decls, it sets the
  /// AvailabilityArgsMatch to false if availability attributes have
  /// different versions, etc.
  static bool AttributesMatch(const Decl *Decl1, const Decl *Decl2,
                              bool &AvailabilityArgsMatch) {
    if (!Decl1->hasAttrs() || !Decl2->hasAttrs()) {
      AvailabilityArgsMatch = (Decl1->hasAttrs() == Decl2->hasAttrs());
      return true;
    }
    AvailabilityArgsMatch = true;
    const AttrVec &Attrs1 = Decl1->getAttrs();
    const AttrVec &Attrs2 = Decl2->getAttrs();
    bool match = MatchTwoAttributeLists(Attrs1, Attrs2, AvailabilityArgsMatch);
    if (match && (Attrs2.size() > Attrs1.size()))
      return MatchTwoAttributeLists(Attrs2, Attrs1, AvailabilityArgsMatch);
    return match;
  }
  
  static bool IsValidIdentifier(ASTContext &Ctx,
                                const char *Name) {
    if (!isIdentifierHead(Name[0]))
      return false;
    std::string NameString = Name;
    NameString[0] = toLowercase(NameString[0]);
    IdentifierInfo *II = &Ctx.Idents.get(NameString);
    return II->getTokenID() ==  tok::identifier;
  }
  
  bool ObjCMigrateASTConsumer::migrateProperty(ASTContext &Ctx,
                               ObjCContainerDecl *D,
                               ObjCMethodDecl *Method) {
    if (Method->isPropertyAccessor() || !Method->isInstanceMethod() ||
        Method->param_size() != 0)
      return false;
    // Is this method candidate to be a getter?
    QualType GRT = Method->getReturnType();
    if (GRT->isVoidType())
      return false;
  
    Selector GetterSelector = Method->getSelector();
    ObjCInstanceTypeFamily OIT_Family =
      Selector::getInstTypeMethodFamily(GetterSelector);
  
    if (OIT_Family != OIT_None)
      return false;
  
    IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);
    Selector SetterSelector =
    SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
                                           PP.getSelectorTable(),
                                           getterName);
    ObjCMethodDecl *SetterMethod = D->getInstanceMethod(SetterSelector);
    unsigned LengthOfPrefix = 0;
    if (!SetterMethod) {
      // try a different naming convention for getter: isXxxxx
      StringRef getterNameString = getterName->getName();
      bool IsPrefix = getterNameString.startswith("is");
      // Note that we don't want to change an isXXX method of retainable object
      // type to property (readonly or otherwise).
      if (IsPrefix && GRT->isObjCRetainableType())
        return false;
      if (IsPrefix || getterNameString.startswith("get")) {
        LengthOfPrefix = (IsPrefix ? 2 : 3);
        const char *CGetterName = getterNameString.data() + LengthOfPrefix;
        // Make sure that first character after "is" or "get" prefix can
        // start an identifier.
        if (!IsValidIdentifier(Ctx, CGetterName))
          return false;
        if (CGetterName[0] && isUppercase(CGetterName[0])) {
          getterName = &Ctx.Idents.get(CGetterName);
          SetterSelector =
          SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
                                                 PP.getSelectorTable(),
                                                 getterName);
          SetterMethod = D->getInstanceMethod(SetterSelector);
        }
      }
    }
  
    if (SetterMethod) {
      if ((ASTMigrateActions & FrontendOptions::ObjCMT_ReadwriteProperty) == 0)
        return false;
      bool AvailabilityArgsMatch;
      if (SetterMethod->isDeprecated() ||
          !AttributesMatch(Method, SetterMethod, AvailabilityArgsMatch))
        return false;
  
      // Is this a valid setter, matching the target getter?
      QualType SRT = SetterMethod->getReturnType();
      if (!SRT->isVoidType())
        return false;
      const ParmVarDecl *argDecl = *SetterMethod->param_begin();
      QualType ArgType = argDecl->getType();
      if (!Ctx.hasSameUnqualifiedType(ArgType, GRT))
        return false;
      edit::Commit commit(*Editor);
      rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit,
                            LengthOfPrefix,
                            (ASTMigrateActions &
                             FrontendOptions::ObjCMT_AtomicProperty) != 0,
                            (ASTMigrateActions &
                             FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0,
                            AvailabilityArgsMatch);
      Editor->commit(commit);
      return true;
    }
    else if (ASTMigrateActions & FrontendOptions::ObjCMT_ReadonlyProperty) {
      // Try a non-void method with no argument (and no setter or property of same name
      // as a 'readonly' property.
      edit::Commit commit(*Editor);
      rewriteToObjCProperty(Method, nullptr /*SetterMethod*/, *NSAPIObj, commit,
                            LengthOfPrefix,
                            (ASTMigrateActions &
                             FrontendOptions::ObjCMT_AtomicProperty) != 0,
                            (ASTMigrateActions &
                             FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0,
                            /*AvailabilityArgsMatch*/false);
      Editor->commit(commit);
      return true;
    }
    return false;
  }
  
  void ObjCMigrateASTConsumer::migrateNsReturnsInnerPointer(ASTContext &Ctx,
                                                            ObjCMethodDecl *OM) {
    if (OM->isImplicit() ||
        !OM->isInstanceMethod() ||
        OM->hasAttr<ObjCReturnsInnerPointerAttr>())
      return;
  
    QualType RT = OM->getReturnType();
    if (!TypeIsInnerPointer(RT) ||
        !NSAPIObj->isMacroDefined("NS_RETURNS_INNER_POINTER"))
      return;
  
    edit::Commit commit(*Editor);
    commit.insertBefore(OM->getEndLoc(), " NS_RETURNS_INNER_POINTER");
    Editor->commit(commit);
  }
  
  void ObjCMigrateASTConsumer::migratePropertyNsReturnsInnerPointer(ASTContext &Ctx,
                                                                    ObjCPropertyDecl *P) {
    QualType T = P->getType();
  
    if (!TypeIsInnerPointer(T) ||
        !NSAPIObj->isMacroDefined("NS_RETURNS_INNER_POINTER"))
      return;
    edit::Commit commit(*Editor);
    commit.insertBefore(P->getEndLoc(), " NS_RETURNS_INNER_POINTER ");
    Editor->commit(commit);
  }
  
  void ObjCMigrateASTConsumer::migrateAllMethodInstaceType(ASTContext &Ctx,
                                                   ObjCContainerDecl *CDecl) {
    if (CDecl->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(CDecl))
      return;
  
    // migrate methods which can have instancetype as their result type.
    for (auto *Method : CDecl->methods()) {
      if (Method->isDeprecated())
        continue;
      migrateMethodInstanceType(Ctx, CDecl, Method);
    }
  }
  
  void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx,
                                                    ObjCContainerDecl *CDecl,
                                                    ObjCMethodDecl *OM,
                                                    ObjCInstanceTypeFamily OIT_Family) {
    if (OM->isInstanceMethod() ||
        OM->getReturnType() == Ctx.getObjCInstanceType() ||
        !OM->getReturnType()->isObjCIdType())
      return;
  
    // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class
    // NSYYYNamE with matching names be at least 3 characters long.
    ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
    if (!IDecl) {
      if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
        IDecl = CatDecl->getClassInterface();
      else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
        IDecl = ImpDecl->getClassInterface();
    }
    if (!IDecl)
      return;
  
    std::string StringClassName = IDecl->getName();
    StringRef LoweredClassName(StringClassName);
    std::string StringLoweredClassName = LoweredClassName.lower();
    LoweredClassName = StringLoweredClassName;
  
    IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0);
    // Handle method with no name at its first selector slot; e.g. + (id):(int)x.
    if (!MethodIdName)
      return;
  
    std::string MethodName = MethodIdName->getName();
    if (OIT_Family == OIT_Singleton || OIT_Family == OIT_ReturnsSelf) {
      StringRef STRefMethodName(MethodName);
      size_t len = 0;
      if (STRefMethodName.startswith("standard"))
        len = strlen("standard");
      else if (STRefMethodName.startswith("shared"))
        len = strlen("shared");
      else if (STRefMethodName.startswith("default"))
        len = strlen("default");
      else
        return;
      MethodName = STRefMethodName.substr(len);
    }
    std::string MethodNameSubStr = MethodName.substr(0, 3);
    StringRef MethodNamePrefix(MethodNameSubStr);
    std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower();
    MethodNamePrefix = StringLoweredMethodNamePrefix;
    size_t Ix = LoweredClassName.rfind(MethodNamePrefix);
    if (Ix == StringRef::npos)
      return;
    std::string ClassNamePostfix = LoweredClassName.substr(Ix);
    StringRef LoweredMethodName(MethodName);
    std::string StringLoweredMethodName = LoweredMethodName.lower();
    LoweredMethodName = StringLoweredMethodName;
    if (!LoweredMethodName.startswith(ClassNamePostfix))
      return;
    if (OIT_Family == OIT_ReturnsSelf)
      ReplaceWithClasstype(*this, OM);
    else
      ReplaceWithInstancetype(Ctx, *this, OM);
  }
  
  static bool IsVoidStarType(QualType Ty) {
    if (!Ty->isPointerType())
      return false;
  
    while (const TypedefType *TD = dyn_cast<TypedefType>(Ty.getTypePtr()))
      Ty = TD->getDecl()->getUnderlyingType();
  
    // Is the type void*?
    const PointerType* PT = Ty->getAs<PointerType>();
    if (PT->getPointeeType().getUnqualifiedType()->isVoidType())
      return true;
    return IsVoidStarType(PT->getPointeeType());
  }
  
  /// AuditedType - This routine audits the type AT and returns false if it is one of known
  /// CF object types or of the "void *" variety. It returns true if we don't care about the type
  /// such as a non-pointer or pointers which have no ownership issues (such as "int *").
  static bool AuditedType (QualType AT) {
    if (!AT->isAnyPointerType() && !AT->isBlockPointerType())
      return true;
    // FIXME. There isn't much we can say about CF pointer type; or is there?
    if (ento::coreFoundation::isCFObjectRef(AT) ||
        IsVoidStarType(AT) ||
        // If an ObjC object is type, assuming that it is not a CF function and
        // that it is an un-audited function.
        AT->isObjCObjectPointerType() || AT->isObjCBuiltinType())
      return false;
    // All other pointers are assumed audited as harmless.
    return true;
  }
  
  void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) {
    if (CFFunctionIBCandidates.empty())
      return;
    if (!NSAPIObj->isMacroDefined("CF_IMPLICIT_BRIDGING_ENABLED")) {
      CFFunctionIBCandidates.clear();
      FileId = FileID();
      return;
    }
    // Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED
    const Decl *FirstFD = CFFunctionIBCandidates[0];
    const Decl *LastFD  =
      CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1];
    const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n";
    edit::Commit commit(*Editor);
    commit.insertBefore(FirstFD->getBeginLoc(), PragmaString);
    PragmaString = "\n\nCF_IMPLICIT_BRIDGING_DISABLED\n";
    SourceLocation EndLoc = LastFD->getEndLoc();
    // get location just past end of function location.
    EndLoc = PP.getLocForEndOfToken(EndLoc);
    if (isa<FunctionDecl>(LastFD)) {
      // For Methods, EndLoc points to the ending semcolon. So,
      // not of these extra work is needed.
      Token Tok;
      // get locaiton of token that comes after end of function.
      bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true);
      if (!Failed)
        EndLoc = Tok.getLocation();
    }
    commit.insertAfterToken(EndLoc, PragmaString);
    Editor->commit(commit);
    FileId = FileID();
    CFFunctionIBCandidates.clear();
  }
  
  void ObjCMigrateASTConsumer::migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl) {
    if (Decl->isDeprecated())
      return;
  
    if (Decl->hasAttr<CFAuditedTransferAttr>()) {
      assert(CFFunctionIBCandidates.empty() &&
             "Cannot have audited functions/methods inside user "
             "provided CF_IMPLICIT_BRIDGING_ENABLE");
      return;
    }
  
    // Finction must be annotated first.
    if (const FunctionDecl *FuncDecl = dyn_cast<FunctionDecl>(Decl)) {
      CF_BRIDGING_KIND AuditKind = migrateAddFunctionAnnotation(Ctx, FuncDecl);
      if (AuditKind == CF_BRIDGING_ENABLE) {
        CFFunctionIBCandidates.push_back(Decl);
        if (FileId.isInvalid())
          FileId = PP.getSourceManager().getFileID(Decl->getLocation());
      }
      else if (AuditKind == CF_BRIDGING_MAY_INCLUDE) {
        if (!CFFunctionIBCandidates.empty()) {
          CFFunctionIBCandidates.push_back(Decl);
          if (FileId.isInvalid())
            FileId = PP.getSourceManager().getFileID(Decl->getLocation());
        }
      }
      else
        AnnotateImplicitBridging(Ctx);
    }
    else {
      migrateAddMethodAnnotation(Ctx, cast<ObjCMethodDecl>(Decl));
      AnnotateImplicitBridging(Ctx);
    }
  }
  
  void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx,
                                                const CallEffects &CE,
                                                const FunctionDecl *FuncDecl,
                                                bool ResultAnnotated) {
    // Annotate function.
    if (!ResultAnnotated) {
      RetEffect Ret = CE.getReturnValue();
      const char *AnnotationString = nullptr;
      if (Ret.getObjKind() == RetEffect::CF) {
        if (Ret.isOwned() && NSAPIObj->isMacroDefined("CF_RETURNS_RETAINED"))
          AnnotationString = " CF_RETURNS_RETAINED";
        else if (Ret.notOwned() &&
                 NSAPIObj->isMacroDefined("CF_RETURNS_NOT_RETAINED"))
          AnnotationString = " CF_RETURNS_NOT_RETAINED";
      }
      else if (Ret.getObjKind() == RetEffect::ObjC) {
        if (Ret.isOwned() && NSAPIObj->isMacroDefined("NS_RETURNS_RETAINED"))
          AnnotationString = " NS_RETURNS_RETAINED";
      }
  
      if (AnnotationString) {
        edit::Commit commit(*Editor);
        commit.insertAfterToken(FuncDecl->getEndLoc(), AnnotationString);
        Editor->commit(commit);
      }
    }
    ArrayRef<ArgEffect> AEArgs = CE.getArgs();
    unsigned i = 0;
    for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),
         pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {
      const ParmVarDecl *pd = *pi;
      ArgEffect AE = AEArgs[i];
      if (AE == DecRef && !pd->hasAttr<CFConsumedAttr>() &&
          NSAPIObj->isMacroDefined("CF_CONSUMED")) {
        edit::Commit commit(*Editor);
        commit.insertBefore(pd->getLocation(), "CF_CONSUMED ");
        Editor->commit(commit);
      }
      else if (AE == DecRefMsg && !pd->hasAttr<NSConsumedAttr>() &&
               NSAPIObj->isMacroDefined("NS_CONSUMED")) {
        edit::Commit commit(*Editor);
        commit.insertBefore(pd->getLocation(), "NS_CONSUMED ");
        Editor->commit(commit);
      }
    }
  }
  
  ObjCMigrateASTConsumer::CF_BRIDGING_KIND
    ObjCMigrateASTConsumer::migrateAddFunctionAnnotation(
                                                    ASTContext &Ctx,
                                                    const FunctionDecl *FuncDecl) {
    if (FuncDecl->hasBody())
      return CF_BRIDGING_NONE;
  
    CallEffects CE  = CallEffects::getEffect(FuncDecl);
    bool FuncIsReturnAnnotated = (FuncDecl->hasAttr<CFReturnsRetainedAttr>() ||
                                  FuncDecl->hasAttr<CFReturnsNotRetainedAttr>() ||
                                  FuncDecl->hasAttr<NSReturnsRetainedAttr>() ||
                                  FuncDecl->hasAttr<NSReturnsNotRetainedAttr>() ||
                                  FuncDecl->hasAttr<NSReturnsAutoreleasedAttr>());
  
    // Trivial case of when function is annotated and has no argument.
    if (FuncIsReturnAnnotated && FuncDecl->getNumParams() == 0)
      return CF_BRIDGING_NONE;
  
    bool ReturnCFAudited = false;
    if (!FuncIsReturnAnnotated) {
      RetEffect Ret = CE.getReturnValue();
      if (Ret.getObjKind() == RetEffect::CF &&
          (Ret.isOwned() || Ret.notOwned()))
        ReturnCFAudited = true;
      else if (!AuditedType(FuncDecl->getReturnType()))
        return CF_BRIDGING_NONE;
    }
  
    // At this point result type is audited for potential inclusion.
    // Now, how about argument types.
    ArrayRef<ArgEffect> AEArgs = CE.getArgs();
    unsigned i = 0;
    bool ArgCFAudited = false;
    for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),
         pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {
      const ParmVarDecl *pd = *pi;
      ArgEffect AE = AEArgs[i];
      if (AE == DecRef /*CFConsumed annotated*/ || AE == IncRef) {
        if (AE == DecRef && !pd->hasAttr<CFConsumedAttr>())
          ArgCFAudited = true;
        else if (AE == IncRef)
          ArgCFAudited = true;
      }
      else {
        QualType AT = pd->getType();
        if (!AuditedType(AT)) {
          AddCFAnnotations(Ctx, CE, FuncDecl, FuncIsReturnAnnotated);
          return CF_BRIDGING_NONE;
        }
      }
    }
    if (ReturnCFAudited || ArgCFAudited)
      return CF_BRIDGING_ENABLE;
  
    return CF_BRIDGING_MAY_INCLUDE;
  }
  
  void ObjCMigrateASTConsumer::migrateARCSafeAnnotation(ASTContext &Ctx,
                                                   ObjCContainerDecl *CDecl) {
    if (!isa<ObjCInterfaceDecl>(CDecl) || CDecl->isDeprecated())
      return;
  
    // migrate methods which can have instancetype as their result type.
    for (const auto *Method : CDecl->methods())
      migrateCFAnnotation(Ctx, Method);
  }
  
  void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx,
                                                const CallEffects &CE,
                                                const ObjCMethodDecl *MethodDecl,
                                                bool ResultAnnotated) {
    // Annotate function.
    if (!ResultAnnotated) {
      RetEffect Ret = CE.getReturnValue();
      const char *AnnotationString = nullptr;
      if (Ret.getObjKind() == RetEffect::CF) {
        if (Ret.isOwned() && NSAPIObj->isMacroDefined("CF_RETURNS_RETAINED"))
          AnnotationString = " CF_RETURNS_RETAINED";
        else if (Ret.notOwned() &&
                 NSAPIObj->isMacroDefined("CF_RETURNS_NOT_RETAINED"))
          AnnotationString = " CF_RETURNS_NOT_RETAINED";
      }
      else if (Ret.getObjKind() == RetEffect::ObjC) {
        ObjCMethodFamily OMF = MethodDecl->getMethodFamily();
        switch (OMF) {
          case clang::OMF_alloc:
          case clang::OMF_new:
          case clang::OMF_copy:
          case clang::OMF_init:
          case clang::OMF_mutableCopy:
            break;
  
          default:
            if (Ret.isOwned() && NSAPIObj->isMacroDefined("NS_RETURNS_RETAINED"))
              AnnotationString = " NS_RETURNS_RETAINED";
            break;
        }
      }
  
      if (AnnotationString) {
        edit::Commit commit(*Editor);
        commit.insertBefore(MethodDecl->getEndLoc(), AnnotationString);
        Editor->commit(commit);
      }
    }
    ArrayRef<ArgEffect> AEArgs = CE.getArgs();
    unsigned i = 0;
    for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(),
         pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) {
      const ParmVarDecl *pd = *pi;
      ArgEffect AE = AEArgs[i];
      if (AE == DecRef && !pd->hasAttr<CFConsumedAttr>() &&
          NSAPIObj->isMacroDefined("CF_CONSUMED")) {
        edit::Commit commit(*Editor);
        commit.insertBefore(pd->getLocation(), "CF_CONSUMED ");
        Editor->commit(commit);
      }
    }
  }
  
  void ObjCMigrateASTConsumer::migrateAddMethodAnnotation(
                                              ASTContext &Ctx,
                                              const ObjCMethodDecl *MethodDecl) {
    if (MethodDecl->hasBody() || MethodDecl->isImplicit())
      return;
  
    CallEffects CE  = CallEffects::getEffect(MethodDecl);
    bool MethodIsReturnAnnotated = (MethodDecl->hasAttr<CFReturnsRetainedAttr>() ||
                                    MethodDecl->hasAttr<CFReturnsNotRetainedAttr>() ||
                                    MethodDecl->hasAttr<NSReturnsRetainedAttr>() ||
                                    MethodDecl->hasAttr<NSReturnsNotRetainedAttr>() ||
                                    MethodDecl->hasAttr<NSReturnsAutoreleasedAttr>());
  
    if (CE.getReceiver() == DecRefMsg &&
        !MethodDecl->hasAttr<NSConsumesSelfAttr>() &&
        MethodDecl->getMethodFamily() != OMF_init &&
        MethodDecl->getMethodFamily() != OMF_release &&
        NSAPIObj->isMacroDefined("NS_CONSUMES_SELF")) {
      edit::Commit commit(*Editor);
      commit.insertBefore(MethodDecl->getEndLoc(), " NS_CONSUMES_SELF");
      Editor->commit(commit);
    }
  
    // Trivial case of when function is annotated and has no argument.
    if (MethodIsReturnAnnotated &&
        (MethodDecl->param_begin() == MethodDecl->param_end()))
      return;
  
    if (!MethodIsReturnAnnotated) {
      RetEffect Ret = CE.getReturnValue();
      if ((Ret.getObjKind() == RetEffect::CF ||
           Ret.getObjKind() == RetEffect::ObjC) &&
          (Ret.isOwned() || Ret.notOwned())) {
        AddCFAnnotations(Ctx, CE, MethodDecl, false);
        return;
      } else if (!AuditedType(MethodDecl->getReturnType()))
        return;
    }
  
    // At this point result type is either annotated or audited.
    // Now, how about argument types.
    ArrayRef<ArgEffect> AEArgs = CE.getArgs();
    unsigned i = 0;
    for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(),
         pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) {
      const ParmVarDecl *pd = *pi;
      ArgEffect AE = AEArgs[i];
      if ((AE == DecRef && !pd->hasAttr<CFConsumedAttr>()) || AE == IncRef ||
          !AuditedType(pd->getType())) {
        AddCFAnnotations(Ctx, CE, MethodDecl, MethodIsReturnAnnotated);
        return;
      }
    }
  }
  
  namespace {
  class SuperInitChecker : public RecursiveASTVisitor<SuperInitChecker> {
  public:
    bool shouldVisitTemplateInstantiations() const { return false; }
    bool shouldWalkTypesOfTypeLocs() const { return false; }
  
    bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
      if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) {
        if (E->getMethodFamily() == OMF_init)
          return false;
      }
      return true;
    }
  };
  } // end anonymous namespace
  
  static bool hasSuperInitCall(const ObjCMethodDecl *MD) {
    return !SuperInitChecker().TraverseStmt(MD->getBody());
  }
  
  void ObjCMigrateASTConsumer::inferDesignatedInitializers(
      ASTContext &Ctx,
      const ObjCImplementationDecl *ImplD) {
  
    const ObjCInterfaceDecl *IFace = ImplD->getClassInterface();
    if (!IFace || IFace->hasDesignatedInitializers())
      return;
    if (!NSAPIObj->isMacroDefined("NS_DESIGNATED_INITIALIZER"))
      return;
  
    for (const auto *MD : ImplD->instance_methods()) {
      if (MD->isDeprecated() ||
          MD->getMethodFamily() != OMF_init ||
          MD->isDesignatedInitializerForTheInterface())
        continue;
      const ObjCMethodDecl *IFaceM = IFace->getMethod(MD->getSelector(),
                                                      /*isInstance=*/true);
      if (!IFaceM)
        continue;
      if (hasSuperInitCall(MD)) {
        edit::Commit commit(*Editor);
        commit.insert(IFaceM->getEndLoc(), " NS_DESIGNATED_INITIALIZER");
        Editor->commit(commit);
      }
    }
  }
  
  bool ObjCMigrateASTConsumer::InsertFoundation(ASTContext &Ctx,
                                                SourceLocation  Loc) {
    if (FoundationIncluded)
      return true;
    if (Loc.isInvalid())
      return false;
    auto *nsEnumId = &Ctx.Idents.get("NS_ENUM");
    if (PP.getMacroDefinitionAtLoc(nsEnumId, Loc)) {
      FoundationIncluded = true;
      return true;
    }
    edit::Commit commit(*Editor);
    if (Ctx.getLangOpts().Modules)
      commit.insert(Loc, "#ifndef NS_ENUM\n@import Foundation;\n#endif\n");
    else
      commit.insert(Loc, "#ifndef NS_ENUM\n#import <Foundation/Foundation.h>\n#endif\n");
    Editor->commit(commit);
    FoundationIncluded = true;
    return true;
  }
  
  namespace {
  
  class RewritesReceiver : public edit::EditsReceiver {
    Rewriter &Rewrite;
  
  public:
    RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
  
    void insert(SourceLocation loc, StringRef text) override {
      Rewrite.InsertText(loc, text);
    }
    void replace(CharSourceRange range, StringRef text) override {
      Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
    }
  };
  
  class JSONEditWriter : public edit::EditsReceiver {
    SourceManager &SourceMgr;
    llvm::raw_ostream &OS;
  
  public:
    JSONEditWriter(SourceManager &SM, llvm::raw_ostream &OS)
      : SourceMgr(SM), OS(OS) {
      OS << "[\n";
    }
    ~JSONEditWriter() override { OS << "]\n"; }
  
  private:
    struct EntryWriter {
      SourceManager &SourceMgr;
      llvm::raw_ostream &OS;
  
      EntryWriter(SourceManager &SM, llvm::raw_ostream &OS)
        : SourceMgr(SM), OS(OS) {
        OS << " {\n";
      }
      ~EntryWriter() {
        OS << " },\n";
      }
  
      void writeLoc(SourceLocation Loc) {
        FileID FID;
        unsigned Offset;
        std::tie(FID, Offset) = SourceMgr.getDecomposedLoc(Loc);
        assert(FID.isValid());
        SmallString<200> Path =
            StringRef(SourceMgr.getFileEntryForID(FID)->getName());
        llvm::sys::fs::make_absolute(Path);
        OS << "  \"file\": \"";
        OS.write_escaped(Path.str()) << "\",\n";
        OS << "  \"offset\": " << Offset << ",\n";
      }
  
      void writeRemove(CharSourceRange Range) {
        assert(Range.isCharRange());
        std::pair<FileID, unsigned> Begin =
            SourceMgr.getDecomposedLoc(Range.getBegin());
        std::pair<FileID, unsigned> End =
            SourceMgr.getDecomposedLoc(Range.getEnd());
        assert(Begin.first == End.first);
        assert(Begin.second <= End.second);
        unsigned Length = End.second - Begin.second;
  
        OS << "  \"remove\": " << Length << ",\n";
      }
  
      void writeText(StringRef Text) {
        OS << "  \"text\": \"";
        OS.write_escaped(Text) << "\",\n";
      }
    };
  
    void insert(SourceLocation Loc, StringRef Text) override {
      EntryWriter Writer(SourceMgr, OS);
      Writer.writeLoc(Loc);
      Writer.writeText(Text);
    }
  
    void replace(CharSourceRange Range, StringRef Text) override {
      EntryWriter Writer(SourceMgr, OS);
      Writer.writeLoc(Range.getBegin());
      Writer.writeRemove(Range);
      Writer.writeText(Text);
    }
  
    void remove(CharSourceRange Range) override {
      EntryWriter Writer(SourceMgr, OS);
      Writer.writeLoc(Range.getBegin());
      Writer.writeRemove(Range);
    }
  };
  
  } // end anonymous namespace
  
  void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
  
    TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
    if (ASTMigrateActions & FrontendOptions::ObjCMT_MigrateDecls) {
      for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();
           D != DEnd; ++D) {
        FileID FID = PP.getSourceManager().getFileID((*D)->getLocation());
        if (FID.isValid())
          if (FileId.isValid() && FileId != FID) {
            if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
              AnnotateImplicitBridging(Ctx);
          }
  
        if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))
          if (canModify(CDecl))
            migrateObjCContainerDecl(Ctx, CDecl);
        if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(*D)) {
          if (canModify(CatDecl))
            migrateObjCContainerDecl(Ctx, CatDecl);
        }
        else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D)) {
          ObjCProtocolDecls.insert(PDecl->getCanonicalDecl());
          if (canModify(PDecl))
            migrateObjCContainerDecl(Ctx, PDecl);
        }
        else if (const ObjCImplementationDecl *ImpDecl =
                 dyn_cast<ObjCImplementationDecl>(*D)) {
          if ((ASTMigrateActions & FrontendOptions::ObjCMT_ProtocolConformance) &&
              canModify(ImpDecl))
            migrateProtocolConformance(Ctx, ImpDecl);
        }
        else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {
          if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros))
            continue;
          if (!canModify(ED))
            continue;
          DeclContext::decl_iterator N = D;
          if (++N != DEnd) {
            const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N);
            if (migrateNSEnumDecl(Ctx, ED, TD) && TD)
              D++;
          }
          else
            migrateNSEnumDecl(Ctx, ED, /*TypedefDecl */nullptr);
        }
        else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*D)) {
          if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros))
            continue;
          if (!canModify(TD))
            continue;
          DeclContext::decl_iterator N = D;
          if (++N == DEnd)
            continue;
          if (const EnumDecl *ED = dyn_cast<EnumDecl>(*N)) {
            if (canModify(ED)) {
              if (++N != DEnd)
                if (const TypedefDecl *TDF = dyn_cast<TypedefDecl>(*N)) {
                  // prefer typedef-follows-enum to enum-follows-typedef pattern.
                  if (migrateNSEnumDecl(Ctx, ED, TDF)) {
                    ++D; ++D;
                    CacheObjCNSIntegerTypedefed(TD);
                    continue;
                  }
                }
              if (migrateNSEnumDecl(Ctx, ED, TD)) {
                ++D;
                continue;
              }
            }
          }
          CacheObjCNSIntegerTypedefed(TD);
        }
        else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D)) {
          if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
              canModify(FD))
            migrateCFAnnotation(Ctx, FD);
        }
  
        if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) {
          bool CanModify = canModify(CDecl);
          // migrate methods which can have instancetype as their result type.
          if ((ASTMigrateActions & FrontendOptions::ObjCMT_Instancetype) &&
              CanModify)
            migrateAllMethodInstaceType(Ctx, CDecl);
          // annotate methods with CF annotations.
          if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
              CanModify)
            migrateARCSafeAnnotation(Ctx, CDecl);
        }
  
        if (const ObjCImplementationDecl *
              ImplD = dyn_cast<ObjCImplementationDecl>(*D)) {
          if ((ASTMigrateActions & FrontendOptions::ObjCMT_DesignatedInitializer) &&
              canModify(ImplD))
            inferDesignatedInitializers(Ctx, ImplD);
        }
      }
      if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
        AnnotateImplicitBridging(Ctx);
    }
  
   if (IsOutputFile) {
     std::error_code EC;
     llvm::raw_fd_ostream OS(MigrateDir, EC, llvm::sys::fs::F_None);
     if (EC) {
        DiagnosticsEngine &Diags = Ctx.getDiagnostics();
        Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
            << EC.message();
        return;
      }
  
     JSONEditWriter Writer(Ctx.getSourceManager(), OS);
     Editor->applyRewrites(Writer);
     return;
   }
  
    Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
    RewritesReceiver Rec(rewriter);
    Editor->applyRewrites(Rec);
  
    for (Rewriter::buffer_iterator
          I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
      FileID FID = I->first;
      RewriteBuffer &buf = I->second;
      const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
      assert(file);
      SmallString<512> newText;
      llvm::raw_svector_ostream vecOS(newText);
      buf.write(vecOS);
      std::unique_ptr<llvm::MemoryBuffer> memBuf(
          llvm::MemoryBuffer::getMemBufferCopy(
              StringRef(newText.data(), newText.size()), file->getName()));
      SmallString<64> filePath(file->getName());
      FileMgr.FixupRelativePath(filePath);
      Remapper.remap(filePath.str(), std::move(memBuf));
    }
  
    if (IsOutputFile) {
      Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
    } else {
      Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
    }
  }
  
  bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
    CI.getDiagnostics().setIgnoreAllWarnings(true);
    return true;
  }
  
  static std::vector<std::string> getWhiteListFilenames(StringRef DirPath) {
    using namespace llvm::sys::fs;
    using namespace llvm::sys::path;
  
    std::vector<std::string> Filenames;
    if (DirPath.empty() || !is_directory(DirPath))
      return Filenames;
  
    std::error_code EC;
    directory_iterator DI = directory_iterator(DirPath, EC);
    directory_iterator DE;
    for (; !EC && DI != DE; DI = DI.increment(EC)) {
      if (is_regular_file(DI->path()))
        Filenames.push_back(filename(DI->path()));
    }
  
    return Filenames;
  }
  
  std::unique_ptr<ASTConsumer>
  MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
    PPConditionalDirectiveRecord *
      PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
    unsigned ObjCMTAction = CI.getFrontendOpts().ObjCMTAction;
    unsigned ObjCMTOpts = ObjCMTAction;
    // These are companion flags, they do not enable transformations.
    ObjCMTOpts &= ~(FrontendOptions::ObjCMT_AtomicProperty |
                    FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty);
    if (ObjCMTOpts == FrontendOptions::ObjCMT_None) {
      // If no specific option was given, enable literals+subscripting transforms
      // by default.
      ObjCMTAction |= FrontendOptions::ObjCMT_Literals |
                      FrontendOptions::ObjCMT_Subscripting;
    }
    CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec));
    std::vector<std::string> WhiteList =
      getWhiteListFilenames(CI.getFrontendOpts().ObjCMTWhiteListPath);
    return llvm::make_unique<ObjCMigrateASTConsumer>(
        CI.getFrontendOpts().OutputFile, ObjCMTAction, Remapper,
        CI.getFileManager(), PPRec, CI.getPreprocessor(),
        /*isOutputFile=*/true, WhiteList);
  }
  
  namespace {
  struct EditEntry {
    const FileEntry *File;
    unsigned Offset;
    unsigned RemoveLen;
    std::string Text;
  
    EditEntry() : File(), Offset(), RemoveLen() {}
  };
  } // end anonymous namespace
  
  namespace llvm {
  template<> struct DenseMapInfo<EditEntry> {
    static inline EditEntry getEmptyKey() {
      EditEntry Entry;
      Entry.Offset = unsigned(-1);
      return Entry;
    }
    static inline EditEntry getTombstoneKey() {
      EditEntry Entry;
      Entry.Offset = unsigned(-2);
      return Entry;
    }
    static unsigned getHashValue(const EditEntry& Val) {
      llvm::FoldingSetNodeID ID;
      ID.AddPointer(Val.File);
      ID.AddInteger(Val.Offset);
      ID.AddInteger(Val.RemoveLen);
      ID.AddString(Val.Text);
      return ID.ComputeHash();
    }
    static bool isEqual(const EditEntry &LHS, const EditEntry &RHS) {
      return LHS.File == RHS.File &&
          LHS.Offset == RHS.Offset &&
          LHS.RemoveLen == RHS.RemoveLen &&
          LHS.Text == RHS.Text;
    }
  };
  } // end namespace llvm
  
  namespace {
  class RemapFileParser {
    FileManager &FileMgr;
  
  public:
    RemapFileParser(FileManager &FileMgr) : FileMgr(FileMgr) { }
  
    bool parse(StringRef File, SmallVectorImpl<EditEntry> &Entries) {
      using namespace llvm::yaml;
  
      llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
          llvm::MemoryBuffer::getFile(File);
      if (!FileBufOrErr)
        return true;
  
      llvm::SourceMgr SM;
      Stream YAMLStream(FileBufOrErr.get()->getMemBufferRef(), SM);
      document_iterator I = YAMLStream.begin();
      if (I == YAMLStream.end())
        return true;
      Node *Root = I->getRoot();
      if (!Root)
        return true;
  
      SequenceNode *SeqNode = dyn_cast<SequenceNode>(Root);
      if (!SeqNode)
        return true;
  
      for (SequenceNode::iterator
             AI = SeqNode->begin(), AE = SeqNode->end(); AI != AE; ++AI) {
        MappingNode *MapNode = dyn_cast<MappingNode>(&*AI);
        if (!MapNode)
          continue;
        parseEdit(MapNode, Entries);
      }
  
      return false;
    }
  
  private:
    void parseEdit(llvm::yaml::MappingNode *Node,
                   SmallVectorImpl<EditEntry> &Entries) {
      using namespace llvm::yaml;
      EditEntry Entry;
      bool Ignore = false;
  
      for (MappingNode::iterator
             KVI = Node->begin(), KVE = Node->end(); KVI != KVE; ++KVI) {
        ScalarNode *KeyString = dyn_cast<ScalarNode>((*KVI).getKey());
        if (!KeyString)
          continue;
        SmallString<10> KeyStorage;
        StringRef Key = KeyString->getValue(KeyStorage);
  
        ScalarNode *ValueString = dyn_cast<ScalarNode>((*KVI).getValue());
        if (!ValueString)
          continue;
        SmallString<64> ValueStorage;
        StringRef Val = ValueString->getValue(ValueStorage);
  
        if (Key == "file") {
          const FileEntry *FE = FileMgr.getFile(Val);
          if (!FE)
            Ignore = true;
          Entry.File = FE;
        } else if (Key == "offset") {
          if (Val.getAsInteger(10, Entry.Offset))
            Ignore = true;
        } else if (Key == "remove") {
          if (Val.getAsInteger(10, Entry.RemoveLen))
            Ignore = true;
        } else if (Key == "text") {
          Entry.Text = Val;
        }
      }
  
      if (!Ignore)
        Entries.push_back(Entry);
    }
  };
  } // end anonymous namespace
  
  static bool reportDiag(const Twine &Err, DiagnosticsEngine &Diag) {
    Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
        << Err.str();
    return true;
  }
  
  static std::string applyEditsToTemp(const FileEntry *FE,
                                      ArrayRef<EditEntry> Edits,
                                      FileManager &FileMgr,
                                      DiagnosticsEngine &Diag) {
    using namespace llvm::sys;
  
    SourceManager SM(Diag, FileMgr);
    FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User);
    LangOptions LangOpts;
    edit::EditedSource Editor(SM, LangOpts);
    for (ArrayRef<EditEntry>::iterator
          I = Edits.begin(), E = Edits.end(); I != E; ++I) {
      const EditEntry &Entry = *I;
      assert(Entry.File == FE);
      SourceLocation Loc =
          SM.getLocForStartOfFile(FID).getLocWithOffset(Entry.Offset);
      CharSourceRange Range;
      if (Entry.RemoveLen != 0) {
        Range = CharSourceRange::getCharRange(Loc,
                                           Loc.getLocWithOffset(Entry.RemoveLen));
      }
  
      edit::Commit commit(Editor);
      if (Range.isInvalid()) {
        commit.insert(Loc, Entry.Text);
      } else if (Entry.Text.empty()) {
        commit.remove(Range);
      } else {
        commit.replace(Range, Entry.Text);
      }
      Editor.commit(commit);
    }
  
    Rewriter rewriter(SM, LangOpts);
    RewritesReceiver Rec(rewriter);
    Editor.applyRewrites(Rec, /*adjustRemovals=*/false);
  
    const RewriteBuffer *Buf = rewriter.getRewriteBufferFor(FID);
    SmallString<512> NewText;
    llvm::raw_svector_ostream OS(NewText);
    Buf->write(OS);
  
    SmallString<64> TempPath;
    int FD;
    if (fs::createTemporaryFile(path::filename(FE->getName()),
                                path::extension(FE->getName()).drop_front(), FD,
                                TempPath)) {
      reportDiag("Could not create file: " + TempPath.str(), Diag);
      return std::string();
    }
  
    llvm::raw_fd_ostream TmpOut(FD, /*shouldClose=*/true);
    TmpOut.write(NewText.data(), NewText.size());
    TmpOut.close();
  
    return TempPath.str();
  }
  
  bool arcmt::getFileRemappingsFromFileList(
                          std::vector<std::pair<std::string,std::string> > &remap,
                          ArrayRef<StringRef> remapFiles,
                          DiagnosticConsumer *DiagClient) {
    bool hasErrorOccurred = false;
  
    FileSystemOptions FSOpts;
    FileManager FileMgr(FSOpts);
    RemapFileParser Parser(FileMgr);
  
    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
        new DiagnosticsEngine(DiagID, new DiagnosticOptions,
                              DiagClient, /*ShouldOwnClient=*/false));
  
    typedef llvm::DenseMap<const FileEntry *, std::vector<EditEntry> >
        FileEditEntriesTy;
    FileEditEntriesTy FileEditEntries;
  
    llvm::DenseSet<EditEntry> EntriesSet;
  
    for (ArrayRef<StringRef>::iterator
           I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) {
      SmallVector<EditEntry, 16> Entries;
      if (Parser.parse(*I, Entries))
        continue;
  
      for (SmallVectorImpl<EditEntry>::iterator
             EI = Entries.begin(), EE = Entries.end(); EI != EE; ++EI) {
        EditEntry &Entry = *EI;
        if (!Entry.File)
          continue;
        std::pair<llvm::DenseSet<EditEntry>::iterator, bool>
          Insert = EntriesSet.insert(Entry);
        if (!Insert.second)
          continue;
  
        FileEditEntries[Entry.File].push_back(Entry);
      }
    }
  
    for (FileEditEntriesTy::iterator
           I = FileEditEntries.begin(), E = FileEditEntries.end(); I != E; ++I) {
      std::string TempFile = applyEditsToTemp(I->first, I->second,
                                              FileMgr, *Diags);
      if (TempFile.empty()) {
        hasErrorOccurred = true;
        continue;
      }
  
      remap.emplace_back(I->first->getName(), TempFile);
    }
  
    return hasErrorOccurred;
  }
  