Add new code migrator support for migrating existing Objective-C code to use
the new Objective-C NSArray/NSDictionary/NSNumber literal syntax.

This introduces a new library, libEdit, which provides a new way to support
migration of code that improves on the original ARC migrator.  We now believe
that most of its functionality can be refactored into the existing libraries,
and thus this new library may shortly disappear.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152141 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/ARCMigrate/ARCMT.cpp b/lib/ARCMigrate/ARCMT.cpp
index 6175307..07f22c4 100644
--- a/lib/ARCMigrate/ARCMT.cpp
+++ b/lib/ARCMigrate/ARCMT.cpp
@@ -398,13 +398,51 @@
   if (err)
     return true;
 
-  CompilerInvocation CI;
-  remapper.applyMappings(CI);
-  remap = CI.getPreprocessorOpts().RemappedFiles;
+  PreprocessorOptions PPOpts;
+  remapper.applyMappings(PPOpts);
+  remap = PPOpts.RemappedFiles;
 
   return false;
 }
 
+bool arcmt::getFileRemappingsFromFileList(
+                        std::vector<std::pair<std::string,std::string> > &remap,
+                        ArrayRef<StringRef> remapFiles,
+                        DiagnosticConsumer *DiagClient) {
+  bool hasErrorOccurred = false;
+  llvm::StringMap<bool> Uniquer;
+
+  llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+  llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
+      new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
+
+  for (ArrayRef<StringRef>::iterator
+         I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) {
+    StringRef file = *I;
+
+    FileRemapper remapper;
+    bool err = remapper.initFromFile(file, *Diags,
+                                     /*ignoreIfFilesChanged=*/true);
+    hasErrorOccurred = hasErrorOccurred || err;
+    if (err)
+      continue;
+
+    PreprocessorOptions PPOpts;
+    remapper.applyMappings(PPOpts);
+    for (PreprocessorOptions::remapped_file_iterator
+           RI = PPOpts.remapped_file_begin(), RE = PPOpts.remapped_file_end();
+           RI != RE; ++RI) {
+      bool &inserted = Uniquer[RI->first];
+      if (inserted)
+        continue;
+      inserted = true;
+      remap.push_back(*RI);
+    }
+  }
+
+  return hasErrorOccurred;
+}
+
 //===----------------------------------------------------------------------===//
 // CollectTransformActions.
 //===----------------------------------------------------------------------===//
@@ -504,7 +542,7 @@
   CInvok.reset(createInvocationForMigration(OrigCI));
   CInvok->getDiagnosticOpts().IgnoreWarnings = true;
 
-  Remapper.applyMappings(*CInvok);
+  Remapper.applyMappings(CInvok->getPreprocessorOpts());
 
   CapturedDiagList capturedDiags;
   std::vector<SourceLocation> ARCMTMacroLocs;
diff --git a/lib/ARCMigrate/CMakeLists.txt b/lib/ARCMigrate/CMakeLists.txt
index 1a64b12..fcb7f72 100644
--- a/lib/ARCMigrate/CMakeLists.txt
+++ b/lib/ARCMigrate/CMakeLists.txt
@@ -4,6 +4,7 @@
   ARCMT.cpp
   ARCMTActions.cpp
   FileRemapper.cpp
+  ObjCMT.cpp
   PlistReporter.cpp
   TransAPIUses.cpp
   TransARCAssign.cpp
diff --git a/lib/ARCMigrate/FileRemapper.cpp b/lib/ARCMigrate/FileRemapper.cpp
index 0e6e35c..1e97c9e 100644
--- a/lib/ARCMigrate/FileRemapper.cpp
+++ b/lib/ARCMigrate/FileRemapper.cpp
@@ -8,8 +8,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ARCMigrate/FileRemapper.h"
-#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/PreprocessorOptions.h"
 #include "clang/Basic/FileManager.h"
+#include "clang/Basic/Diagnostic.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/FileSystem.h"
@@ -50,9 +51,15 @@
 
 bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
                                 bool ignoreIfFilesChanged) {
+  std::string infoFile = getRemapInfoFile(outputDir);
+  return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
+}
+
+bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
+                                bool ignoreIfFilesChanged) {
   assert(FromToMappings.empty() &&
          "initFromDisk should be called before any remap calls");
-  std::string infoFile = getRemapInfoFile(outputDir);
+  std::string infoFile = filePath;
   bool fileExists = false;
   llvm::sys::fs::exists(infoFile, fileExists);
   if (!fileExists)
@@ -108,8 +115,15 @@
   if (fs::create_directory(outputDir, existed) != llvm::errc::success)
     return report("Could not create directory: " + outputDir, Diag);
 
-  std::string errMsg;
   std::string infoFile = getRemapInfoFile(outputDir);
+  return flushToFile(infoFile, Diag);
+}
+
+bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
+  using namespace llvm::sys;
+
+  std::string errMsg;
+  std::string infoFile = outputPath;
   llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg,
                                llvm::raw_fd_ostream::F_Binary);
   if (!errMsg.empty())
@@ -189,8 +203,7 @@
   return false;
 }
 
-void FileRemapper::applyMappings(CompilerInvocation &CI) const {
-  PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
+void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
   for (MappingsTy::const_iterator
          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
@@ -204,8 +217,7 @@
   PPOpts.RetainRemappedFileBuffers = true;
 }
 
-void FileRemapper::transferMappingsAndClear(CompilerInvocation &CI) {
-  PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
+void FileRemapper::transferMappingsAndClear(PreprocessorOptions &PPOpts) {
   for (MappingsTy::iterator
          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp
new file mode 100644
index 0000000..497377a
--- /dev/null
+++ b/lib/ARCMigrate/ObjCMT.cpp
@@ -0,0 +1,226 @@
+//===--- 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 "clang/ARCMigrate/ARCMTActions.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/NSAPI.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/Edit/Rewriters.h"
+#include "clang/Edit/EditedSource.h"
+#include "clang/Edit/Commit.h"
+#include "clang/Edit/EditsReceiver.h"
+#include "clang/Rewrite/Rewriter.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Basic/FileManager.h"
+#include "llvm/ADT/SmallString.h"
+
+using namespace clang;
+using namespace arcmt;
+
+namespace {
+
+class ObjCMigrateASTConsumer : public ASTConsumer {
+  void migrateDecl(Decl *D);
+
+public:
+  std::string MigrateDir;
+  bool MigrateLiterals;
+  bool MigrateSubscripting;
+  llvm::OwningPtr<NSAPI> NSAPIObj;
+  llvm::OwningPtr<edit::EditedSource> Editor;
+  FileRemapper &Remapper;
+  FileManager &FileMgr;
+  const PreprocessingRecord *PPRec;
+  bool IsOutputFile;
+
+  ObjCMigrateASTConsumer(StringRef migrateDir,
+                         bool migrateLiterals,
+                         bool migrateSubscripting,
+                         FileRemapper &remapper,
+                         FileManager &fileMgr,
+                         const PreprocessingRecord *PPRec,
+                         bool isOutputFile = false)
+  : MigrateDir(migrateDir),
+    MigrateLiterals(migrateLiterals),
+    MigrateSubscripting(migrateSubscripting),
+    Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec),
+    IsOutputFile(isOutputFile) { }
+
+protected:
+  virtual void Initialize(ASTContext &Context) {
+    NSAPIObj.reset(new NSAPI(Context));
+    Editor.reset(new edit::EditedSource(Context.getSourceManager(),
+                                        Context.getLangOptions(),
+                                        PPRec));
+  }
+
+  virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
+    for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
+      migrateDecl(*I);
+    return true;
+  }
+  virtual void HandleInterestingDecl(DeclGroupRef DG) {
+    // Ignore decls from the PCH.
+  }
+  virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
+    ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
+  }
+
+  virtual void HandleTranslationUnit(ASTContext &Ctx);
+};
+
+}
+
+ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction,
+                             StringRef migrateDir,
+                             bool migrateLiterals,
+                             bool migrateSubscripting)
+  : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir),
+    MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting),
+    CompInst(0) {
+  if (MigrateDir.empty())
+    MigrateDir = "."; // user current directory if none is given.
+}
+
+ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI,
+                                                  StringRef InFile) {
+  ASTConsumer *
+    WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
+  ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir,
+                                                       MigrateLiterals,
+                                                       MigrateSubscripting,
+                                                       Remapper,
+                                                    CompInst->getFileManager(),
+                          CompInst->getPreprocessor().getPreprocessingRecord()); 
+  ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer };
+  return new MultiplexConsumer(Consumers);
+}
+
+bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
+  Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
+                        /*ignoreIfFilesChanges=*/true);
+  CompInst = &CI;
+  CI.getDiagnostics().setIgnoreAllWarnings(true);
+  CI.getPreprocessorOpts().DetailedRecord = true;
+  CI.getPreprocessorOpts().DetailedRecordConditionalDirectives = true;
+  return true;
+}
+
+namespace {
+class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
+  ObjCMigrateASTConsumer &Consumer;
+
+public:
+  ObjCMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
+
+  bool shouldVisitTemplateInstantiations() const { return false; }
+  bool shouldWalkTypesOfTypeLocs() const { return false; }
+
+  bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
+    if (Consumer.MigrateLiterals) {
+      edit::Commit commit(*Consumer.Editor);
+      edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit);
+      Consumer.Editor->commit(commit);
+    }
+
+    if (Consumer.MigrateSubscripting) {
+      edit::Commit commit(*Consumer.Editor);
+      edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
+      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::child_range range = E->children(); range; ++range)
+      if (!TraverseStmt(*range))
+        return false;
+
+    return WalkUpFromObjCMessageExpr(E);
+  }
+};
+}
+
+void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
+  if (!D)
+    return;
+  if (isa<ObjCMethodDecl>(D))
+    return; // Wait for the ObjC container declaration.
+
+  ObjCMigrator(*this).TraverseDecl(D);
+}
+
+namespace {
+
+class RewritesReceiver : public edit::EditsReceiver {
+  Rewriter &Rewrite;
+
+public:
+  RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
+
+  virtual void insert(SourceLocation loc, StringRef text) {
+    Rewrite.InsertText(loc, text);
+  }
+  virtual void replace(CharSourceRange range, StringRef text) {
+    Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
+  }
+};
+
+}
+
+void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
+  Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOptions());
+  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);
+    llvm::SmallString<512> newText;
+    llvm::raw_svector_ostream vecOS(newText);
+    buf.write(vecOS);
+    vecOS.flush();
+    llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
+                   StringRef(newText.data(), newText.size()), file->getName());
+    llvm::SmallString<64> filePath(file->getName());
+    FileMgr.FixupRelativePath(filePath);
+    Remapper.remap(filePath.str(), memBuf);
+  }
+
+  if (IsOutputFile) {
+    Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
+  } else {
+    Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
+  }
+}
+
+bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
+  CI.getPreprocessorOpts().DetailedRecord = true;
+  CI.getPreprocessorOpts().DetailedRecordConditionalDirectives = true;
+  return true;
+}
+
+ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI,
+                                                  StringRef InFile) {
+  return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile,
+                                    /*MigrateLiterals=*/true,
+                                    /*MigrateSubscripting=*/true,
+                                    Remapper,
+                                    CI.getFileManager(),
+                                  CI.getPreprocessor().getPreprocessingRecord(),
+                                    /*isOutputFile=*/true); 
+}
diff --git a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
index 7bb7b5e..15669ac 100644
--- a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
+++ b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
@@ -21,6 +21,8 @@
 #include "Internals.h"
 #include "clang/Sema/SemaDiagnostic.h"
 #include "clang/AST/ParentMap.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Basic/SourceManager.h"
 
 using namespace clang;
 using namespace arcmt;
@@ -128,27 +130,105 @@
     Transaction Trans(Pass.TA);
     clearDiagnostics(rec->getExprLoc());
 
-    if (E->getMethodFamily() == OMF_release &&
-        isRemovable(E) && isInAtFinally(E)) {
+    ObjCMessageExpr *Msg = E;
+    Expr *RecContainer = Msg;
+    SourceRange RecRange = rec->getSourceRange();
+    checkForGCDOrXPC(Msg, RecContainer, rec, RecRange);
+
+    if (Msg->getMethodFamily() == OMF_release &&
+        isRemovable(RecContainer) && isInAtFinally(RecContainer)) {
       // Change the -release to "receiver = nil" in a finally to avoid a leak
       // when an exception is thrown.
-      Pass.TA.replace(E->getSourceRange(), rec->getSourceRange());
+      Pass.TA.replace(RecContainer->getSourceRange(), RecRange);
       std::string str = " = ";
       str += getNilString(Pass.Ctx);
-      Pass.TA.insertAfterToken(rec->getLocEnd(), str);
+      Pass.TA.insertAfterToken(RecRange.getEnd(), str);
       return true;
     }
 
-    if (!hasSideEffects(E, Pass.Ctx)) {
-      if (tryRemoving(E))
+    if (!hasSideEffects(rec, Pass.Ctx)) {
+      if (tryRemoving(RecContainer))
         return true;
     }
-    Pass.TA.replace(E->getSourceRange(), rec->getSourceRange());
+    Pass.TA.replace(RecContainer->getSourceRange(), RecRange);
 
     return true;
   }
 
 private:
+  /// \brief Check if the retain/release is due to a GCD/XPC macro that are
+  /// defined as:
+  ///
+  /// #define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; })
+  /// #define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; })
+  /// #define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; })
+  /// #define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; })
+  ///
+  /// and return the top container which is the StmtExpr and the macro argument
+  /// expression.
+  void checkForGCDOrXPC(ObjCMessageExpr *Msg, Expr *&RecContainer,
+                        Expr *&Rec, SourceRange &RecRange) {
+    SourceLocation Loc = Msg->getExprLoc();
+    if (!Loc.isMacroID())
+      return;
+    SourceManager &SM = Pass.Ctx.getSourceManager();
+    StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM,
+                                                     Pass.Ctx.getLangOptions());
+    bool isGCDOrXPC = llvm::StringSwitch<bool>(MacroName)
+        .Case("dispatch_retain", true)
+        .Case("dispatch_release", true)
+        .Case("xpc_retain", true)
+        .Case("xpc_release", true)
+        .Default(false);
+    if (!isGCDOrXPC)
+      return;
+
+    StmtExpr *StmtE = 0;
+    Stmt *S = Msg;
+    while (S) {
+      if (StmtExpr *SE = dyn_cast<StmtExpr>(S)) {
+        StmtE = SE;
+        break;
+      }
+      S = StmtMap->getParent(S);
+    }
+
+    if (!StmtE)
+      return;
+
+    Stmt::child_range StmtExprChild = StmtE->children();
+    if (!StmtExprChild)
+      return;
+    CompoundStmt *CompS = dyn_cast_or_null<CompoundStmt>(*StmtExprChild);
+    if (!CompS)
+      return;
+
+    Stmt::child_range CompStmtChild = CompS->children();
+    if (!CompStmtChild)
+      return;
+    DeclStmt *DeclS = dyn_cast_or_null<DeclStmt>(*CompStmtChild);
+    if (!DeclS)
+      return;
+    if (!DeclS->isSingleDecl())
+      return;
+    VarDecl *VD = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl());
+    if (!VD)
+      return;
+    Expr *Init = VD->getInit();
+    if (!Init)
+      return;
+
+    RecContainer = StmtE;
+    Rec = Init->IgnoreParenImpCasts();
+    if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Rec))
+      Rec = EWC->getSubExpr()->IgnoreParenImpCasts();
+    RecRange = Rec->getSourceRange();
+    if (SM.isMacroArgExpansion(RecRange.getBegin()))
+      RecRange.setBegin(SM.getImmediateSpellingLoc(RecRange.getBegin()));
+    if (SM.isMacroArgExpansion(RecRange.getEnd()))
+      RecRange.setEnd(SM.getImmediateSpellingLoc(RecRange.getEnd()));
+  }
+
   void clearDiagnostics(SourceLocation loc) const {
     Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message,
                             diag::err_unavailable,