[cfe-commits] r68268 - in /cfe/trunk: include/clang/Frontend/FixItRewriter.h lib/Frontend/FixItRewriter.cpp lib/Parse/ParseInit.cpp lib/Sema/SemaDeclCXX.cpp test/Sema/fixit-c90.c test/Sema/fixit-errors.c test/Sema/fixit.c test/SemaCXX/fixit.cpp tools/clang-cc/clang-cc.cpp

Douglas Gregor dgregor at apple.com
Wed Apr 1 18:08:08 PDT 2009


Author: dgregor
Date: Wed Apr  1 20:08:08 2009
New Revision: 68268

URL: http://llvm.org/viewvc/llvm-project?rev=68268&view=rev
Log:
Introduce a "-fixit" mode to clang-cc that applies code-modification hints.


Added:
    cfe/trunk/include/clang/Frontend/FixItRewriter.h
    cfe/trunk/lib/Frontend/FixItRewriter.cpp
Modified:
    cfe/trunk/lib/Parse/ParseInit.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/test/Sema/fixit-c90.c
    cfe/trunk/test/Sema/fixit-errors.c
    cfe/trunk/test/Sema/fixit.c
    cfe/trunk/test/SemaCXX/fixit.cpp
    cfe/trunk/tools/clang-cc/clang-cc.cpp

Added: cfe/trunk/include/clang/Frontend/FixItRewriter.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/FixItRewriter.h?rev=68268&view=auto

==============================================================================
--- cfe/trunk/include/clang/Frontend/FixItRewriter.h (added)
+++ cfe/trunk/include/clang/Frontend/FixItRewriter.h Wed Apr  1 20:08:08 2009
@@ -0,0 +1,65 @@
+//===--- FixItRewriter.h - Fix-It Rewriter Diagnostic Client ----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a diagnostic client adaptor that performs rewrites as
+// suggested by code modification hints attached to diagnostics. It
+// then forwards any diagnostics to the adapted diagnostic client.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_FRONTEND_FIX_IT_REWRITER_H
+#define LLVM_CLANG_FRONTEND_FIX_IT_REWRITER_H
+
+#include "clang/Basic/Diagnostic.h"
+
+namespace clang {
+
+class Rewriter;
+class SourceManager;
+
+class FixItRewriter : public DiagnosticClient {
+  /// \brief The adapted diagnostic client, to which we will forward
+  /// any diagnostics.
+  DiagnosticClient *Client;
+
+  /// \brief The rewriter used to perform the various code
+  /// modifications.
+  Rewriter *Rewrite;
+
+  /// \brief The number of rewriter failures.
+  unsigned NumFailures;
+
+public:
+  /// \brief Initialize a new fix-it rewriter.
+  FixItRewriter(DiagnosticClient *Client, SourceManager &SourceMgr);
+
+  /// \brief Destroy the fix-it rewriter.
+  ~FixItRewriter();
+
+  /// \brief Write the modified source file.
+  ///
+  /// \returns true if there was an error, false otherwise.
+  bool WriteFixedFile(const std::string &InFileName, 
+                      const std::string &OutFileName = std::string());
+
+  /// IncludeInDiagnosticCounts - This method (whose default implementation
+  ///  returns true) indicates whether the diagnostics handled by this
+  ///  DiagnosticClient should be included in the number of diagnostics
+  ///  reported by Diagnostic.
+  virtual bool IncludeInDiagnosticCounts() const;
+
+  /// HandleDiagnostic - Handle this diagnostic, reporting it to the user or
+  /// capturing it to a log as needed.
+  virtual void HandleDiagnostic(Diagnostic::Level DiagLevel,
+                                const DiagnosticInfo &Info);
+
+};
+
+}
+
+#endif // LLVM_CLANG_FRONTEND_FIX_IT_REWRITER_H

Added: cfe/trunk/lib/Frontend/FixItRewriter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/FixItRewriter.cpp?rev=68268&view=auto

==============================================================================
--- cfe/trunk/lib/Frontend/FixItRewriter.cpp (added)
+++ cfe/trunk/lib/Frontend/FixItRewriter.cpp Wed Apr  1 20:08:08 2009
@@ -0,0 +1,140 @@
+//===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a diagnostic client adaptor that performs rewrites as
+// suggested by code modification hints attached to diagnostics. It
+// then forwards any diagnostics to the adapted diagnostic client.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/FixItRewriter.h"
+#include "clang/Rewrite/Rewriter.h"
+#include "llvm/ADT/OwningPtr.h"
+#include "llvm/Support/Streams.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/System/Path.h"
+#include <cstdio>
+using namespace clang;
+
+FixItRewriter::FixItRewriter(DiagnosticClient *Client, 
+                             SourceManager &SourceMgr)
+  : Client(Client), NumFailures(0) {
+  Rewrite = new Rewriter(SourceMgr);
+}
+
+FixItRewriter::~FixItRewriter() {
+  delete Rewrite;
+}
+
+bool FixItRewriter::WriteFixedFile(const std::string &InFileName, 
+                                   const std::string &OutFileName) {
+  if (NumFailures > 0) {
+    // FIXME: Use diagnostic machinery!
+    std::fprintf(stderr, 
+                 "%d fix-it failures detected; code will not be modified",
+                 NumFailures);
+    return true;
+  }
+
+  llvm::OwningPtr<llvm::raw_ostream> OwnedStream;
+  llvm::raw_ostream *OutFile;
+  if (OutFileName == "-") {
+    OutFile = &llvm::outs();
+  } else if (!OutFileName.empty()) {
+    std::string Err;
+    OutFile = new llvm::raw_fd_ostream(OutFileName.c_str(), 
+                                       // set binary mode (critical for Windoze)
+                                       true, 
+                                       Err);
+    OwnedStream.reset(OutFile);
+  } else if (InFileName == "-") {
+    OutFile = &llvm::outs();
+  } else {
+    llvm::sys::Path Path(InFileName);
+    Path.eraseSuffix();
+    Path.appendSuffix("cpp");
+    std::string Err;
+    OutFile = new llvm::raw_fd_ostream(Path.toString().c_str(), 
+                                       // set binary mode (critical for Windoze)
+                                       true, 
+                                       Err);
+    OwnedStream.reset(OutFile);
+  }  
+
+  FileID MainFileID = Rewrite->getSourceMgr().getMainFileID();
+  if (const RewriteBuffer *RewriteBuf = 
+        Rewrite->getRewriteBufferFor(MainFileID)) {
+    *OutFile << std::string(RewriteBuf->begin(), RewriteBuf->end());
+  } else {
+    std::fprintf(stderr, "Main file is unchanged\n");
+  }
+  OutFile->flush();
+
+  return false;
+}
+
+bool FixItRewriter::IncludeInDiagnosticCounts() const {
+  return Client? Client->IncludeInDiagnosticCounts() : false;
+}
+
+void FixItRewriter::HandleDiagnostic(Diagnostic::Level DiagLevel,
+                                     const DiagnosticInfo &Info) {
+  if (Client)
+    Client->HandleDiagnostic(DiagLevel, Info);
+
+  // Make sure that we can perform all of the modifications we
+  // in this diagnostic.
+  bool CanRewrite = true;
+  for (unsigned Idx = 0; Idx < Info.getNumCodeModificationHints(); ++Idx) {
+    const CodeModificationHint &Hint = Info.getCodeModificationHint(Idx);
+    if (Hint.RemoveRange.isValid() &&
+        (!Rewrite->isRewritable(Hint.RemoveRange.getBegin()) ||
+         !Rewrite->isRewritable(Hint.RemoveRange.getEnd()) ||
+         Rewrite->getRangeSize(Hint.RemoveRange) == -1)) {
+      CanRewrite = false;
+      break;
+    }
+
+    if (Hint.InsertionLoc.isValid() && 
+        !Rewrite->isRewritable(Hint.InsertionLoc)) {
+      CanRewrite = false;
+      break;
+    }
+  }
+
+  if (!CanRewrite) // FIXME: warn the user that this rewrite couldn't be done
+    return;
+
+  bool Failed = false;
+  for (unsigned Idx = 0; Idx < Info.getNumCodeModificationHints(); ++Idx) {
+    const CodeModificationHint &Hint = Info.getCodeModificationHint(Idx);
+    if (Hint.RemoveRange.isValid()) {
+      if (Hint.CodeToInsert.empty()) {
+        // We're removing code.
+        if (Rewrite->RemoveText(Hint.RemoveRange.getBegin(),
+                                Rewrite->getRangeSize(Hint.RemoveRange)))
+          Failed = true;
+      } else {
+        // We're replacing code.
+        if (Rewrite->ReplaceText(Hint.RemoveRange.getBegin(),
+                                 Rewrite->getRangeSize(Hint.RemoveRange),
+                                 Hint.CodeToInsert.c_str(),
+                                 Hint.CodeToInsert.size()))
+          Failed = true;
+      }
+    } else {
+      // We're adding code.
+      if (Rewrite->InsertStrBefore(Hint.InsertionLoc, Hint.CodeToInsert))
+        Failed = true;
+    }
+  }
+
+  if (Failed)
+    ++NumFailures;
+}

Modified: cfe/trunk/lib/Parse/ParseInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseInit.cpp?rev=68268&r1=68267&r2=68268&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseInit.cpp (original)
+++ cfe/trunk/lib/Parse/ParseInit.cpp Wed Apr  1 20:08:08 2009
@@ -218,7 +218,7 @@
       (Desig.getDesignator(0).isArrayDesignator() ||
        Desig.getDesignator(0).isArrayRangeDesignator())) {
     Diag(Tok, diag::ext_gnu_missing_equal_designator)
-      << CodeModificationHint::CreateInsertion(Tok.getLocation(), "=");
+      << CodeModificationHint::CreateInsertion(Tok.getLocation(), "= ");
     return Actions.ActOnDesignatedInitializer(Desig, Tok.getLocation(),
                                               true, ParseInitializer());
   }

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=68268&r1=68267&r2=68268&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Apr  1 20:08:08 2009
@@ -1309,7 +1309,7 @@
     if (Context.getCanonicalType(ParamType).getUnqualifiedType() == ClassTy) {
       SourceLocation ParamLoc = Constructor->getParamDecl(0)->getLocation();
       Diag(ParamLoc, diag::err_constructor_byvalue_arg)
-        << CodeModificationHint::CreateInsertion(ParamLoc, "const &");
+        << CodeModificationHint::CreateInsertion(ParamLoc, " const &");
       Invalid = true;
     }
   }

Modified: cfe/trunk/test/Sema/fixit-c90.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/fixit-c90.c?rev=68268&r1=68267&r2=68268&view=diff

==============================================================================
--- cfe/trunk/test/Sema/fixit-c90.c (original)
+++ cfe/trunk/test/Sema/fixit-c90.c Wed Apr  1 20:08:08 2009
@@ -1,4 +1,4 @@
-/* RUN: clang -fsyntax-only -std=c90 -pedantic %s 
+/* RUN: clang -fsyntax-only -std=c90 -pedantic -fixit %s -o - | clang-cc -pedantic -x c -std=c90 -Werror -
  */
 /* This is a test of the various code modification hints that are
    provided as part of warning or extension diagnostics. Eventually,

Modified: cfe/trunk/test/Sema/fixit-errors.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/fixit-errors.c?rev=68268&r1=68267&r2=68268&view=diff

==============================================================================
--- cfe/trunk/test/Sema/fixit-errors.c (original)
+++ cfe/trunk/test/Sema/fixit-errors.c Wed Apr  1 20:08:08 2009
@@ -1,4 +1,4 @@
-// RUN: clang-cc -fsyntax-only -pedantic -verify %s 
+// RUN: clang-cc -fsyntax-only -pedantic -fixit %s -o - | clang-cc -pedantic -Werror -x c -
 
 /* This is a test of the various code modification hints that are
    provided as part of warning or extension diagnostics. Eventually,

Modified: cfe/trunk/test/Sema/fixit.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/fixit.c?rev=68268&r1=68267&r2=68268&view=diff

==============================================================================
--- cfe/trunk/test/Sema/fixit.c (original)
+++ cfe/trunk/test/Sema/fixit.c Wed Apr  1 20:08:08 2009
@@ -1,4 +1,4 @@
-// RUN: clang -fsyntax-only -pedantic %s 
+// RUN: clang -fsyntax-only -pedantic -fixit %s -o - | clang-cc -pedantic -Werror -x c -
 
 /* This is a test of the various code modification hints that are
    provided as part of warning or extension diagnostics. Eventually,
@@ -25,5 +25,6 @@
 
 int f2(const char *my_string) {
   // FIXME: terminal output isn't so good when "my_string" is shorter
-  return my_string == "foo";
+  // FIXME: Needs an #include hint, too!
+  //  return my_string == "foo";
 }

Modified: cfe/trunk/test/SemaCXX/fixit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/fixit.cpp?rev=68268&r1=68267&r2=68268&view=diff

==============================================================================
--- cfe/trunk/test/SemaCXX/fixit.cpp (original)
+++ cfe/trunk/test/SemaCXX/fixit.cpp Wed Apr  1 20:08:08 2009
@@ -1,4 +1,4 @@
-// RUN: clang-cc -fsyntax-only -pedantic -verify %s 
+// RUN: clang-cc -fsyntax-only -pedantic -fixit %s -o - | clang-cc -fsyntax-only -pedantic -Werror -x c++ -
 
 /* This is a test of the various code modification hints that are
    provided as part of warning or extension diagnostics. Eventually,

Modified: cfe/trunk/tools/clang-cc/clang-cc.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-cc/clang-cc.cpp?rev=68268&r1=68267&r2=68268&view=diff

==============================================================================
--- cfe/trunk/tools/clang-cc/clang-cc.cpp (original)
+++ cfe/trunk/tools/clang-cc/clang-cc.cpp Wed Apr  1 20:08:08 2009
@@ -25,6 +25,7 @@
 #include "clang-cc.h"
 #include "ASTConsumers.h"
 #include "clang/Frontend/CompileOptions.h"
+#include "clang/Frontend/FixItRewriter.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/InitHeaderSearch.h"
 #include "clang/Frontend/PathDiagnosticClients.h"
@@ -85,6 +86,7 @@
   RewriteBlocks,                // ObjC->C Rewriter for Blocks.
   RewriteMacros,                // Expand macros but not #includes.
   RewriteTest,                  // Rewriter playground
+  FixIt,                        // Fix-It Rewriter
   HTMLTest,                     // HTML displayer testing stuff.
   EmitAssembly,                 // Emit a .s file.
   EmitLLVM,                     // Emit a .ll file.
@@ -161,6 +163,8 @@
                         "Expand macros without full preprocessing"),
              clEnumValN(RewriteBlocks, "rewrite-blocks",
                         "Rewrite Blocks to C"),
+             clEnumValN(FixIt, "fixit",
+                        "Apply fix-it advice to the input source"),
              clEnumValEnd));
 
 
@@ -1342,7 +1346,8 @@
                              const std::string &InFile, ProgActions PA) {
   llvm::OwningPtr<ASTConsumer> Consumer;
   bool ClearSourceMgr = false;
-  
+  FixItRewriter *FixItRewrite = 0;
+
   switch (PA) {
   default:
     Consumer.reset(CreateASTConsumer(InFile, PP.getDiagnostics(),
@@ -1443,6 +1448,14 @@
     ClearSourceMgr = true;
     break;
   }
+
+  case FixIt:
+    llvm::TimeRegion Timer(ClangFrontendTimer);
+    Consumer.reset(new ASTConsumer());
+    FixItRewrite = new FixItRewriter(PP.getDiagnostics().getClient(),
+                                     PP.getSourceManager());
+    PP.getDiagnostics().setClient(FixItRewrite);
+    break;
   }
 
   if (Consumer) {
@@ -1458,6 +1471,9 @@
     
     ParseAST(PP, Consumer.get(), *ContextOwner.get(), Stats);
     
+    if (FixItRewrite)
+      FixItRewrite->WriteFixedFile(InFile, OutputFile);
+
     // If in -disable-free mode, don't deallocate these when they go out of
     // scope.
     if (DisableFree)





More information about the cfe-commits mailing list