Teach clang -fixit to modify files in-place, or -fixit=suffix to create new
files with the additional suffix in the middle.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@102230 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 3386eff..db937bc 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -361,12 +361,6 @@
     Res.push_back("-cxx-inheritance-view");
     Res.push_back(Opts.ViewClassInheritance);
   }
-  for (unsigned i = 0, e = Opts.FixItLocations.size(); i != e; ++i) {
-    Res.push_back("-fixit-at");
-    Res.push_back(Opts.FixItLocations[i].FileName + ":" +
-                  llvm::utostr(Opts.FixItLocations[i].Line) + ":" +
-                  llvm::utostr(Opts.FixItLocations[i].Column));
-  }
   if (!Opts.CodeCompletionAt.FileName.empty()) {
     Res.push_back("-code-completion-at");
     Res.push_back(Opts.CodeCompletionAt.FileName + ":" +
@@ -910,6 +904,9 @@
       Opts.ProgramAction = frontend::EmitLLVMOnly; break;
     case OPT_emit_obj:
       Opts.ProgramAction = frontend::EmitObj; break;
+    case OPT_fixit_EQ:
+      Opts.FixItSuffix = A->getValue(Args);
+      // fall-through!
     case OPT_fixit:
       Opts.ProgramAction = frontend::FixIt; break;
     case OPT_emit_pch:
@@ -956,20 +953,6 @@
     !Args.hasArg(OPT_no_code_completion_debug_printer);
   Opts.DisableFree = Args.hasArg(OPT_disable_free);
 
-  Opts.FixItLocations.clear();
-  for (arg_iterator it = Args.filtered_begin(OPT_fixit_at),
-         ie = Args.filtered_end(); it != ie; ++it) {
-    const char *Loc = it->getValue(Args);
-    ParsedSourceLocation PSL = ParsedSourceLocation::FromString(Loc);
-
-    if (PSL.FileName.empty()) {
-      Diags.Report(diag::err_drv_invalid_value) << it->getAsString(Args) << Loc;
-      continue;
-    }
-
-    Opts.FixItLocations.push_back(PSL);
-  }
-
   Opts.OutputFile = getLastArgValue(Args, OPT_o);
   Opts.Plugins = getAllArgValues(Args, OPT_load);
   Opts.RelocatablePCH = Args.hasArg(OPT_relocatable_pch);
diff --git a/lib/Frontend/FixItRewriter.cpp b/lib/Frontend/FixItRewriter.cpp
index 7aff923..cb2b44a 100644
--- a/lib/Frontend/FixItRewriter.cpp
+++ b/lib/Frontend/FixItRewriter.cpp
@@ -26,8 +26,12 @@
 using namespace clang;
 
 FixItRewriter::FixItRewriter(Diagnostic &Diags, SourceManager &SourceMgr,
-                             const LangOptions &LangOpts)
-  : Diags(Diags), Rewrite(SourceMgr, LangOpts), NumFailures(0) {
+                             const LangOptions &LangOpts,
+                             FixItPathRewriter *PathRewriter)
+  : Diags(Diags),
+    Rewrite(SourceMgr, LangOpts),
+    PathRewriter(PathRewriter),
+    NumFailures(0) {
   Client = Diags.getClient();
   Diags.setClient(this);
 }
@@ -52,15 +56,15 @@
 
   for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
     const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first);
-    llvm::sys::Path Path(Entry->getName());
-    std::string Suffix = Path.getSuffix();
-    Path.eraseSuffix();
-    Path.appendSuffix("fixit." + Suffix);
+    std::string Filename = Entry->getName();
+    if (PathRewriter)
+      Filename = PathRewriter->RewriteFilename(Filename);
     std::string Err;
-    llvm::raw_fd_ostream OS(Path.c_str(), Err, llvm::raw_fd_ostream::F_Binary);
+    llvm::raw_fd_ostream OS(Filename.c_str(), Err,
+                            llvm::raw_fd_ostream::F_Binary);
     if (!Err.empty()) {
       Diags.Report(clang::diag::err_fe_unable_to_open_output)
-          << Path.c_str() << Err;
+          << Filename << Err;
       continue;
     }
     RewriteBuffer &RewriteBuf = I->second;
@@ -79,48 +83,10 @@
                                      const DiagnosticInfo &Info) {
   Client->HandleDiagnostic(DiagLevel, Info);
 
-  // Skip over any diagnostics that are ignored.
-  if (DiagLevel == Diagnostic::Ignored)
+  // Skip over any diagnostics that are ignored or notes.
+  if (DiagLevel <= Diagnostic::Note)
     return;
 
-  const SourceManager &SM = Rewrite.getSourceMgr();
-  if (!FixItLocations.empty()) {
-    // The user has specified the locations where we should perform
-    // the various fix-it modifications.
-
-    // If this diagnostic does not have any code modifications,
-    // completely ignore it, even if it's an error: fix-it locations
-    // are meant to perform specific fix-ups even in the presence of
-    // other errors.
-    if (Info.getNumFixItHints() == 0)
-      return;
-
-    // See if the location of the error is one that matches what the
-    // user requested.
-    bool AcceptableLocation = false;
-    const FileEntry *File = SM.getFileEntryForID(
-                                                Info.getLocation().getFileID());
-    unsigned Line = Info.getLocation().getSpellingLineNumber();
-    unsigned Column = Info.getLocation().getSpellingColumnNumber();
-    for (llvm::SmallVector<RequestedSourceLocation, 4>::iterator
-           Loc = FixItLocations.begin(), LocEnd = FixItLocations.end();
-         Loc != LocEnd; ++Loc) {
-      if (Loc->File == File &&
-          ((Loc->Line == 0 && Loc->Column == 0 &&
-            DiagLevel > Diagnostic::Note) ||
-           (Loc->Line == Line && Loc->Column == Column))) {
-        AcceptableLocation = true;
-        break;
-      }
-    }
-
-    if (!AcceptableLocation)
-      return;
-  } else if (DiagLevel == Diagnostic::Note) {
-    // Don't apply fix-it modifications in notes.
-    return;
-  }
-
   // Make sure that we can perform all of the modifications we
   // in this diagnostic.
   bool CanRewrite = Info.getNumFixItHints() > 0;
@@ -197,3 +163,5 @@
   Diags.Report(Loc, DiagID);
   Diags.setClient(this);
 }
+
+FixItPathRewriter::~FixItPathRewriter() {}
diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp
index e2b2fd7..6cd960b 100644
--- a/lib/Frontend/FrontendActions.cpp
+++ b/lib/Frontend/FrontendActions.cpp
@@ -19,6 +19,7 @@
 #include "clang/Frontend/FixItRewriter.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/Utils.h"
+#include "llvm/ADT/OwningPtr.h"
 #include "llvm/Support/raw_ostream.h"
 using namespace clang;
 
@@ -113,85 +114,36 @@
   return new ASTConsumer();
 }
 
-/// AddFixItLocations - Add any individual user specified "fix-it" locations,
-/// and return true on success.
-static bool AddFixItLocations(CompilerInstance &CI,
-                              FixItRewriter &FixItRewrite) {
-  const std::vector<ParsedSourceLocation> &Locs =
-    CI.getFrontendOpts().FixItLocations;
-  for (unsigned i = 0, e = Locs.size(); i != e; ++i) {
-    const FileEntry *File = CI.getFileManager().getFile(Locs[i].FileName);
-    if (!File) {
-      CI.getDiagnostics().Report(diag::err_fe_unable_to_find_fixit_file)
-        << Locs[i].FileName;
-      return false;
-    }
+class FixItActionSuffixInserter : public FixItPathRewriter {
+  std::string NewSuffix;
 
-    RequestedSourceLocation Requested;
-    Requested.File = File;
-    Requested.Line = Locs[i].Line;
-    Requested.Column = Locs[i].Column;
-    FixItRewrite.addFixItLocation(Requested);
+public:
+  explicit FixItActionSuffixInserter(std::string NewSuffix)
+    : NewSuffix(NewSuffix) {}
+
+  std::string RewriteFilename(const std::string &Filename) {
+    llvm::sys::Path Path(Filename);
+    std::string Suffix = Path.getSuffix();
+    Path.eraseSuffix();
+    Path.appendSuffix(NewSuffix + "." + Suffix);
+    return Path.c_str();
   }
-
-  const std::string &OutputFile = CI.getFrontendOpts().OutputFile;
-  if (Locs.empty() && !OutputFile.empty()) {
-    // FIXME: we will issue "FIX-IT applied suggested code changes" for every
-    // input, but only the main file will actually be rewritten.
-    const std::vector<std::pair<FrontendOptions::InputKind, std::string> > &Inputs =
-      CI.getFrontendOpts().Inputs;
-    for (unsigned i = 0, e = Inputs.size(); i != e; ++i) {
-      const FileEntry *File = CI.getFileManager().getFile(Inputs[i].second);
-      assert(File && "Input file not found in FileManager");
-      RequestedSourceLocation Requested;
-      Requested.File = File;
-      Requested.Line = 0;
-      Requested.Column = 0;
-      FixItRewrite.addFixItLocation(Requested);
-    }
-  }
-
-  return true;
-}
+};
 
 bool FixItAction::BeginSourceFileAction(CompilerInstance &CI,
                                         llvm::StringRef Filename) {
+  const FrontendOptions &FEOpts = getCompilerInstance().getFrontendOpts();
+  if (!FEOpts.FixItSuffix.empty()) {
+    PathRewriter.reset(new FixItActionSuffixInserter(FEOpts.FixItSuffix));
+  } else {
+    PathRewriter.reset();
+  }
   Rewriter.reset(new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(),
-                                   CI.getLangOpts()));
-  if (!AddFixItLocations(CI, *Rewriter))
-    return false;
-
+                                   CI.getLangOpts(), PathRewriter.get()));
   return true;
 }
 
 void FixItAction::EndSourceFileAction() {
-  const FrontendOptions &FEOpts = getCompilerInstance().getFrontendOpts();
-  if (!FEOpts.OutputFile.empty()) {
-    // When called with 'clang -fixit -o filename' output only the main file.
-
-    const SourceManager &SM = getCompilerInstance().getSourceManager();
-    FileID MainFileID = SM.getMainFileID();
-    if (!Rewriter->IsModified(MainFileID)) {
-      getCompilerInstance().getDiagnostics().Report(
-          diag::note_fixit_main_file_unchanged);
-      return;
-    }
-
-    llvm::OwningPtr<llvm::raw_ostream> OwnedStream;
-    llvm::raw_ostream *OutFile;
-    if (FEOpts.OutputFile == "-") {
-      OutFile = &llvm::outs();
-    } else {
-      std::string Err;
-      OutFile = new llvm::raw_fd_ostream(FEOpts.OutputFile.c_str(), Err,
-                                         llvm::raw_fd_ostream::F_Binary);
-      OwnedStream.reset(OutFile);
-    }
-
-    Rewriter->WriteFixedFile(MainFileID, *OutFile);
-    return;
-  }
-
   // Otherwise rewrite all files.
   Rewriter->WriteFixedFiles();
 }