Introduce 3 new fixit options:

-fixit-recompile
	applies fixits and recompiles the result
-fixit-to-temporary
	applies fixits to temporary files
-fix-only-warnings">,
    applies fixits for warnings only, not errors

Combining "-fixit-recompile -fixit-to-temporary" allows testing the result of fixits
without touching the original sources.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149027 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index aa367b0..7e5af12 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -466,6 +466,12 @@
     Res.push_back("-version");
   if (Opts.FixWhatYouCan)
     Res.push_back("-fix-what-you-can");
+  if (Opts.FixOnlyWarnings)
+    Res.push_back("-fix-only-warnings");
+  if (Opts.FixAndRecompile)
+    Res.push_back("-fixit-recompile");
+  if (Opts.FixToTemporaries)
+    Res.push_back("-fixit-to-temporary");
   switch (Opts.ARCMTAction) {
   case FrontendOptions::ARCMT_None:
     break;
@@ -1401,6 +1407,9 @@
   Opts.ASTMergeFiles = Args.getAllArgValues(OPT_ast_merge);
   Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm);
   Opts.FixWhatYouCan = Args.hasArg(OPT_fix_what_you_can);
+  Opts.FixOnlyWarnings = Args.hasArg(OPT_fix_only_warnings);
+  Opts.FixAndRecompile = Args.hasArg(OPT_fixit_recompile);
+  Opts.FixToTemporaries = Args.hasArg(OPT_fixit_to_temp);
 
   Opts.ARCMTAction = FrontendOptions::ARCMT_None;
   if (const Arg *A = Args.getLastArg(OPT_arcmt_check,
diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 2782a85..9f2bdf2 100644
--- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -87,6 +87,10 @@
   if (!Act)
     return 0;
 
+  if (CI.getFrontendOpts().FixAndRecompile) {
+    Act = new FixItRecompile(Act);
+  }
+  
   // Potentially wrap the base FE action in an ARC Migrate Tool action.
   switch (CI.getFrontendOpts().ARCMTAction) {
   case FrontendOptions::ARCMT_None:
diff --git a/lib/Rewrite/FixItRewriter.cpp b/lib/Rewrite/FixItRewriter.cpp
index 632c0de..5102966 100644
--- a/lib/Rewrite/FixItRewriter.cpp
+++ b/lib/Rewrite/FixItRewriter.cpp
@@ -31,14 +31,16 @@
   : Diags(Diags),
     Rewrite(SourceMgr, LangOpts),
     FixItOpts(FixItOpts),
-    NumFailures(0) {
+    NumFailures(0),
+    PrevDiagSilenced(false) {
+  OwnsClient = Diags.ownsClient();
   Client = Diags.takeClient();
   Diags.setClient(this);
 }
 
 FixItRewriter::~FixItRewriter() {
   Diags.takeClient();
-  Diags.setClient(Client);
+  Diags.setClient(Client, OwnsClient);
 }
 
 bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) {
@@ -49,7 +51,8 @@
   return false;
 }
 
-bool FixItRewriter::WriteFixedFiles() {
+bool FixItRewriter::WriteFixedFiles(
+            std::vector<std::pair<std::string, std::string> > *RewrittenFiles) {
   if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) {
     Diag(FullSourceLoc(), diag::warn_fixit_no_changes);
     return true;
@@ -69,6 +72,9 @@
     RewriteBuffer &RewriteBuf = I->second;
     RewriteBuf.write(OS);
     OS.flush();
+
+    if (RewrittenFiles)
+      RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename));
   }
 
   return false;
@@ -83,11 +89,24 @@
   // Default implementation (Warnings/errors count).
   DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
 
-  Client->HandleDiagnostic(DiagLevel, Info);
+  if (!FixItOpts->Silent ||
+      DiagLevel >= DiagnosticsEngine::Error ||
+      (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) ||
+      (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) {
+    Client->HandleDiagnostic(DiagLevel, Info);
+    PrevDiagSilenced = false;
+  } else {
+    PrevDiagSilenced = true;
+  }
 
   // Skip over any diagnostics that are ignored or notes.
   if (DiagLevel <= DiagnosticsEngine::Note)
     return;
+  // Skip over errors if we are only fixing warnings.
+  if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) {
+    ++NumFailures;
+    return;
+  }
 
   // Make sure that we can perform all of the modifications we
   // in this diagnostic.
@@ -107,8 +126,7 @@
       Diag(Info.getLocation(), diag::note_fixit_in_macro);
 
     // If this was an error, refuse to perform any rewriting.
-    if (DiagLevel == DiagnosticsEngine::Error ||
-          DiagLevel == DiagnosticsEngine::Fatal) {
+    if (DiagLevel >= DiagnosticsEngine::Error) {
       if (++NumFailures == 1)
         Diag(Info.getLocation(), diag::note_fixit_unfixed_error);
     }
diff --git a/lib/Rewrite/FrontendActions.cpp b/lib/Rewrite/FrontendActions.cpp
index f00e7fd..c1568cd 100644
--- a/lib/Rewrite/FrontendActions.cpp
+++ b/lib/Rewrite/FrontendActions.cpp
@@ -12,6 +12,7 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Parse/Parser.h"
 #include "clang/Basic/FileManager.h"
+#include "clang/Frontend/FrontendActions.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/Utils.h"
@@ -21,6 +22,7 @@
 #include "llvm/ADT/OwningPtr.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/FileSystem.h"
 using namespace clang;
 
 //===----------------------------------------------------------------------===//
@@ -64,6 +66,22 @@
     return Path.str();
   }
 };
+
+class FixItRewriteToTemp : public FixItOptions {
+public:
+  std::string RewriteFilename(const std::string &Filename) {
+    llvm::SmallString<128> Path;
+    Path = llvm::sys::path::filename(Filename);
+    Path += "-%%%%%%%%";
+    Path += llvm::sys::path::extension(Filename);
+    int fd;
+    llvm::SmallString<128> NewPath;
+    if (llvm::sys::fs::unique_file(Path.str(), fd, NewPath)
+          == llvm::errc::success)
+      ::close(fd);
+    return NewPath.str();
+  }
+};
 } // end anonymous namespace
 
 bool FixItAction::BeginSourceFileAction(CompilerInstance &CI,
@@ -86,6 +104,45 @@
   Rewriter->WriteFixedFiles();
 }
 
+bool FixItRecompile::BeginInvocation(CompilerInstance &CI) {
+
+  std::vector<std::pair<std::string, std::string> > RewrittenFiles;
+  bool err = false;
+  {
+    const FrontendOptions &FEOpts = CI.getFrontendOpts();
+    llvm::OwningPtr<FrontendAction> FixAction(new SyntaxOnlyAction());
+    FixAction->BeginSourceFile(CI, FEOpts.Inputs[0]);
+
+    llvm::OwningPtr<FixItOptions> FixItOpts;
+    if (FEOpts.FixToTemporaries)
+      FixItOpts.reset(new FixItRewriteToTemp());
+    else
+      FixItOpts.reset(new FixItRewriteInPlace());
+    FixItOpts->Silent = true;
+    FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan;
+    FixItOpts->FixOnlyWarnings = FEOpts.FixOnlyWarnings;
+    FixItRewriter Rewriter(CI.getDiagnostics(), CI.getSourceManager(),
+                           CI.getLangOpts(), FixItOpts.get());
+    FixAction->Execute();
+
+    err = Rewriter.WriteFixedFiles(&RewrittenFiles);
+  
+    FixAction->EndSourceFile();
+    CI.setSourceManager(0);
+    CI.setFileManager(0);
+  }
+  if (err)
+    return false;
+  CI.getDiagnosticClient().clear();
+
+  PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
+  PPOpts.RemappedFiles.insert(PPOpts.RemappedFiles.end(),
+                              RewrittenFiles.begin(), RewrittenFiles.end());
+  PPOpts.RemappedFilesKeepOriginalName = false;
+
+  return true;
+}
+
 //===----------------------------------------------------------------------===//
 // Preprocessor Actions
 //===----------------------------------------------------------------------===//