[llvm-branch-commits] [cfe-branch] r149126 - in /cfe/branches/tooling: include/clang/Tooling/Refactoring.h lib/Tooling/Refactoring.cpp lib/Tooling/Tooling.cpp tools/CMakeLists.txt tools/fix-llvm-style/ tools/fix-llvm-style/CMakeLists.txt tools/fix-llvm-style/FixLLVMStyle.cpp tools/fix-llvm-style/Makefile unittests/Tooling/RefactoringTest.cpp

Manuel Klimek klimek at google.com
Fri Jan 27 01:34:04 PST 2012


Author: klimek
Date: Fri Jan 27 03:34:04 2012
New Revision: 149126

URL: http://llvm.org/viewvc/llvm-project?rev=149126&view=rev
Log:
Implements a first draft of an LLVM style correction tool.

Includes some fixes (and regression tests) to the refactoring
infrastructure.


Added:
    cfe/branches/tooling/tools/fix-llvm-style/
    cfe/branches/tooling/tools/fix-llvm-style/CMakeLists.txt   (with props)
    cfe/branches/tooling/tools/fix-llvm-style/FixLLVMStyle.cpp   (with props)
    cfe/branches/tooling/tools/fix-llvm-style/Makefile   (with props)
Modified:
    cfe/branches/tooling/include/clang/Tooling/Refactoring.h
    cfe/branches/tooling/lib/Tooling/Refactoring.cpp
    cfe/branches/tooling/lib/Tooling/Tooling.cpp
    cfe/branches/tooling/tools/CMakeLists.txt
    cfe/branches/tooling/unittests/Tooling/RefactoringTest.cpp

Modified: cfe/branches/tooling/include/clang/Tooling/Refactoring.h
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/include/clang/Tooling/Refactoring.h?rev=149126&r1=149125&r2=149126&view=diff
==============================================================================
--- cfe/branches/tooling/include/clang/Tooling/Refactoring.h (original)
+++ cfe/branches/tooling/include/clang/Tooling/Refactoring.h Fri Jan 27 03:34:04 2012
@@ -35,6 +35,9 @@
 /// specific file.
 class Replacement {
 public:
+  /// \brief Creates an invalid (not applicable) replacement.
+  Replacement();
+
   /// \brief Creates a replacement of the range [Offset, Offset+Length) in
   /// FilePath with ReplacementText.
   ///
@@ -58,6 +61,18 @@
   Replacement(SourceManager &Sources, const Node &NodeToReplace,
               llvm::StringRef ReplacementText);
 
+  /// \brief Returns whether this replacement can be applied to a file.
+  ///
+  /// Only replacements that are in a valid file can be applied.
+  bool IsApplicable() const;
+
+  /// \brief Accessors.
+  /// @{
+  std::string GetFilePath() const { return FilePath; }
+  unsigned GetOffset() const { return Offset; }
+  unsigned GetLength() const { return Length; }
+  /// @}
+
   /// \brief Applies the replacement on the Rewriter.
   bool Apply(Rewriter &Rewrite) const;
 

Modified: cfe/branches/tooling/lib/Tooling/Refactoring.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/lib/Tooling/Refactoring.cpp?rev=149126&r1=149125&r2=149126&view=diff
==============================================================================
--- cfe/branches/tooling/lib/Tooling/Refactoring.cpp (original)
+++ cfe/branches/tooling/lib/Tooling/Refactoring.cpp Fri Jan 27 03:34:04 2012
@@ -23,10 +23,15 @@
 namespace clang {
 namespace tooling {
 
+static const char * const InvalidLocation = "invalid-location";
+
+Replacement::Replacement()
+  : FilePath(InvalidLocation), Offset(0), Length(0) {}
+
 Replacement::Replacement(llvm::StringRef FilePath, unsigned Offset,
                          unsigned Length, llvm::StringRef ReplacementText)
-    : FilePath(FilePath), Offset(Offset),
-      Length(Length), ReplacementText(ReplacementText) {}
+  : FilePath(FilePath), Offset(Offset),
+    Length(Length), ReplacementText(ReplacementText) {}
 
 Replacement::Replacement(SourceManager &Sources, SourceLocation Start,
                          unsigned Length, llvm::StringRef ReplacementText) {
@@ -38,6 +43,10 @@
   SetFromSourceRange(Sources, Range, ReplacementText);
 }
 
+bool Replacement::IsApplicable() const {
+  return FilePath != InvalidLocation;
+}
+
 bool Replacement::Apply(Rewriter &Rewrite) const {
   SourceManager &SM = Rewrite.getSourceMgr();
   const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
@@ -76,7 +85,7 @@
   const std::pair<FileID, unsigned> DecomposedLocation =
       Sources.getDecomposedLoc(Start);
   const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first);
-  this->FilePath = Entry != NULL ? Entry->getName() : "invalid-location";
+  this->FilePath = Entry != NULL ? Entry->getName() : InvalidLocation;
   this->Offset = DecomposedLocation.second;
   this->Length = Length;
   this->ReplacementText = ReplacementText;
@@ -85,7 +94,7 @@
 void Replacement::SetFromSourceRange(SourceManager &Sources,
                                      const CharSourceRange &Range,
                                      llvm::StringRef ReplacementText) {
-  SetFromSourceLocation(Sources, Range.getBegin(),
+  SetFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()),
                         getRangeSize(Sources, Range), ReplacementText);
 }
 
@@ -94,7 +103,11 @@
   for (Replacements::const_iterator I = Replaces.begin(),
                                     E = Replaces.end();
        I != E; ++I) {
-    Result = I->Apply(Rewrite) && Result;
+    if (I->IsApplicable()) {
+      Result = I->Apply(Rewrite) && Result;
+    } else {
+      Result = false;
+    }
   }
   return Result;
 }
@@ -120,14 +133,14 @@
 }
 
 int getRangeSize(SourceManager &Sources, const CharSourceRange &Range) {
-  std::pair<FileID, unsigned> Start =
-      Sources.getDecomposedLoc(Range.getBegin());
-  std::pair<FileID, unsigned> End =
-      Sources.getDecomposedLoc(Range.getEnd());
+  SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin());
+  SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd());
+  std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
+  std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
   if (Start.first != End.first) return -1;
   if (Range.isTokenRange())
     // FIXME: Bring in the correct LangOptions.
-    End.second += Lexer::MeasureTokenLength(Range.getEnd(), Sources,
+    End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources,
                                             LangOptions());
   return End.second - Start.second;
 }
@@ -148,8 +161,7 @@
   SourceManager Sources(Diagnostics, Tool.GetFiles());
   Rewriter Rewrite(Sources, DefaultLangOptions);
   if (!ApplyAllReplacements(Replace, Rewrite)) {
-    llvm::errs() << "Could not apply replacements.\n";
-    return 1;
+    llvm::errs() << "Skipped some replacements.\n";
   }
   if (!SaveRewrittenFiles(Rewrite)) {
     llvm::errs() << "Could not save rewritten files.\n";

Modified: cfe/branches/tooling/lib/Tooling/Tooling.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/lib/Tooling/Tooling.cpp?rev=149126&r1=149125&r2=149126&view=diff
==============================================================================
--- cfe/branches/tooling/lib/Tooling/Tooling.cpp (original)
+++ cfe/branches/tooling/lib/Tooling/Tooling.cpp Fri Jan 27 03:34:04 2012
@@ -436,7 +436,8 @@
     if (!ErrorMessage.empty()) {
       llvm::outs() << "Error while parsing JSON database: " << ErrorMessage
                    << "\n";
-      exit(1);
+      llvm::outs() << "Skipping " << File << "\n";
+      continue;
     }
     if (!LookupResult.CommandLine.empty()) {
       if (!LookupResult.Directory.empty()) {

Modified: cfe/branches/tooling/tools/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/tools/CMakeLists.txt?rev=149126&r1=149125&r2=149126&view=diff
==============================================================================
--- cfe/branches/tooling/tools/CMakeLists.txt (original)
+++ cfe/branches/tooling/tools/CMakeLists.txt Fri Jan 27 03:34:04 2012
@@ -6,3 +6,4 @@
 add_subdirectory(driver)
 add_subdirectory(clang-check)
 add_subdirectory(remove-cstr-calls)
+add_subdirectory(fix-llvm-style)

Added: cfe/branches/tooling/tools/fix-llvm-style/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/tools/fix-llvm-style/CMakeLists.txt?rev=149126&view=auto
==============================================================================
--- cfe/branches/tooling/tools/fix-llvm-style/CMakeLists.txt (added)
+++ cfe/branches/tooling/tools/fix-llvm-style/CMakeLists.txt Fri Jan 27 03:34:04 2012
@@ -0,0 +1,5 @@
+set(LLVM_USED_LIBS clangTooling clangBasic clangAST)
+
+add_clang_executable(fix-llvm-style
+  FixLLVMStyle.cpp
+  )

Propchange: cfe/branches/tooling/tools/fix-llvm-style/CMakeLists.txt
------------------------------------------------------------------------------
    svn:eol-style = LF

Added: cfe/branches/tooling/tools/fix-llvm-style/FixLLVMStyle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/tools/fix-llvm-style/FixLLVMStyle.cpp?rev=149126&view=auto
==============================================================================
--- cfe/branches/tooling/tools/fix-llvm-style/FixLLVMStyle.cpp (added)
+++ cfe/branches/tooling/tools/fix-llvm-style/FixLLVMStyle.cpp Fri Jan 27 03:34:04 2012
@@ -0,0 +1,165 @@
+//=- tools/fix-llvm-style/FixLLVMStyle.cpp - Automatic LLVM style correction =//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// FIXME: This is an early first draft that needs clean-up.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/system_error.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using clang::tooling::NewFrontendActionFactory;
+using clang::tooling::Replacement;
+
+// FIXME: Pull out helper methods in here into more fitting places.
+
+template <typename T>
+std::string GetFile(const clang::SourceManager& source_manager, const T& node) {
+  clang::SourceLocation start_spelling_location =
+      source_manager.getSpellingLoc(node.getLocStart());
+  if (!start_spelling_location.isValid()) return std::string();
+  clang::FileID file_id = source_manager.getFileID(start_spelling_location);
+  const clang::FileEntry* file_entry =
+      source_manager.getFileEntryForID(file_id);
+  if (file_entry == NULL) return std::string();
+  return file_entry->getName();
+}
+
+// Returns the text that makes up 'node' in the source.
+// Returns an empty string if the text cannot be found.
+static std::string GetText(const SourceManager &SourceManager,
+                           SourceLocation LocStart, SourceLocation LocEnd) {
+  SourceLocation StartSpellingLocatino =
+      SourceManager.getSpellingLoc(LocStart);
+  SourceLocation EndSpellingLocation =
+      SourceManager.getSpellingLoc(LocEnd);
+  if (!StartSpellingLocatino.isValid() || !EndSpellingLocation.isValid()) {
+    return std::string();
+  }
+  bool Invalid = true;
+  const char *Text =
+    SourceManager.getCharacterData(StartSpellingLocatino, &Invalid);
+  if (Invalid) {
+    return std::string();
+  }
+  std::pair<FileID, unsigned> Start =
+      SourceManager.getDecomposedLoc(StartSpellingLocatino);
+  std::pair<FileID, unsigned> End =
+      SourceManager.getDecomposedLoc(Lexer::getLocForEndOfToken(
+          EndSpellingLocation, 0, SourceManager, LangOptions()));
+  if (Start.first != End.first) {
+    // Start and end are in different files.
+    return std::string();
+  }
+  if (End.second < Start.second) {
+    // Shuffling text with macros may cause this.
+    return std::string();
+  }
+  return std::string(Text, End.second - Start.second);
+}
+
+template <typename T>
+static std::string GetText(const SourceManager &SourceManager, const T &Node) {
+  return GetText(SourceManager, Node.getLocStart(), Node.getLocEnd());
+}
+
+namespace {
+class FixLLVMStyle: public ast_matchers::MatchFinder::MatchCallback {
+ public:
+  FixLLVMStyle(tooling::Replacements *Replace)
+      : Replace(Replace), EditFilesExpression(".*Tooling/.*") {}
+
+  virtual void Run(const ast_matchers::MatchFinder::MatchResult &Result) {
+    if (const CallExpr *Call = Result.Nodes.GetStmtAs<CallExpr>("call")) {
+      llvm::errs() << "Skipping: "
+                   << GetText(*Result.SourceManager, *Call) << "\n";
+      return;
+    }
+    Replacement ReplaceText;
+    std::string Name;
+    std::string OldName;
+    std::string SedCommand;
+    if (const FunctionDecl *Declaration =
+          Result.Nodes.GetDeclAs<FunctionDecl>("declaration")) {
+      Name = Declaration->getNameAsString();
+      OldName = Name;
+      if (const CXXMethodDecl *Method =
+            llvm::dyn_cast<CXXMethodDecl>(Declaration)) {
+        if (Method->size_overridden_methods() > 0) {
+          llvm::errs() << "Skipping: " << OldName << "\n";
+          return;
+        }
+      }
+      if (isupper(Name[0])) {
+        Name[0] = tolower(Name[0]);
+        if (Name == "new") Name = "create";
+
+        if (const Expr *Callee = Result.Nodes.GetStmtAs<Expr>("callee")) {
+          if (!EditFilesExpression.match(GetFile(*Result.SourceManager,
+                                                 *Declaration))) {
+            llvm::errs() << "Skipping: " << OldName << "\n";
+            return;
+          }
+          std::string CalleeText = GetText(*Result.SourceManager, *Callee);
+          std::string ReplacementText =
+            CalleeText.substr(0, CalleeText.size() - OldName.size()) + Name;
+          ReplaceText = Replacement(*Result.SourceManager, Callee,
+                                    ReplacementText);
+        } else {
+          DeclarationNameInfo NameInfo = Declaration->getNameInfo();
+          ReplaceText = Replacement(*Result.SourceManager, &NameInfo, Name);
+        }
+      }
+    }
+    if (EditFilesExpression.match(ReplaceText.GetFilePath())) {
+      llvm::outs() << "s/" << OldName << "/" << Name << "/g;\n";
+      Replace->insert(ReplaceText);
+    }
+  }
+
+ private:
+  tooling::Replacements *Replace;
+  llvm::Regex EditFilesExpression;
+};
+} // end namespace
+
+int main(int argc, char **argv) {
+  tooling::RefactoringTool Tool(argc, argv);
+  ast_matchers::MatchFinder Finder;
+  Finder.AddMatcher(
+      StatementMatcher(AnyOf(
+        // Match method or function calls.
+        Call(Callee(Id("declaration", Function())),
+             Callee(Id("callee", Expression()))),
+        // Match any calls to catch cases we can't handle yet.
+        Id("call", Call()))),
+      new FixLLVMStyle(&Tool.GetReplacements()));
+
+  Finder.AddMatcher(
+      DeclarationMatcher(AllOf(
+        Id("declaration", Function()),
+        Not(Constructor()))),
+      new FixLLVMStyle(&Tool.GetReplacements()));
+  return Tool.Run(NewFrontendActionFactory(&Finder));
+}
+

Propchange: cfe/branches/tooling/tools/fix-llvm-style/FixLLVMStyle.cpp
------------------------------------------------------------------------------
    svn:eol-style = LF

Added: cfe/branches/tooling/tools/fix-llvm-style/Makefile
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/tools/fix-llvm-style/Makefile?rev=149126&view=auto
==============================================================================
--- cfe/branches/tooling/tools/fix-llvm-style/Makefile (added)
+++ cfe/branches/tooling/tools/fix-llvm-style/Makefile Fri Jan 27 03:34:04 2012
@@ -0,0 +1,23 @@
+##===- tools/remove-cstr-calls/Makefile --------------------*- Makefile -*-===##
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL := ../..
+
+TOOLNAME = fix-llvm-style
+NO_INSTALL = 1
+
+# No plugins, optimize startup time.
+TOOL_NO_EXPORTS = 1
+
+LINK_COMPONENTS := support mc
+USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
+           clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \
+           clangAST.a clangASTMatchers.a clangLex.a clangBasic.a
+
+include $(CLANG_LEVEL)/Makefile

Propchange: cfe/branches/tooling/tools/fix-llvm-style/Makefile
------------------------------------------------------------------------------
    svn:eol-style = LF

Modified: cfe/branches/tooling/unittests/Tooling/RefactoringTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/unittests/Tooling/RefactoringTest.cpp?rev=149126&r1=149125&r2=149126&view=diff
==============================================================================
--- cfe/branches/tooling/unittests/Tooling/RefactoringTest.cpp (original)
+++ cfe/branches/tooling/unittests/Tooling/RefactoringTest.cpp Fri Jan 27 03:34:04 2012
@@ -7,14 +7,20 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
 #include "clang/Tooling/Refactoring.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/DiagnosticOptions.h"
+#include "clang/Frontend/FrontendAction.h"
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Rewrite/Rewriter.h"
+#include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_os_ostream.h"
@@ -102,7 +108,7 @@
 
 TEST_F(ReplacementTest, CanReplaceTextAtPosition) {
   FileID ID = Context.CreateInMemoryFile("input.cpp",
-		                         "line1\nline2\nline3\nline4");
+                                         "line1\nline2\nline3\nline4");
   SourceLocation Location = Context.GetLocation(ID, 2, 3);
   Replacement Replace(Context.CreateReplacement(Location, 12, "x"));
   EXPECT_TRUE(Replace.Apply(Context.Rewrite));
@@ -130,6 +136,19 @@
   EXPECT_FALSE(Replace.Apply(Context.Rewrite));
 }
 
+TEST_F(ReplacementTest, CanRetrivePath) {
+  Replacement Replace("/path/to/file.cpp", 0, 1, "");
+  EXPECT_EQ("/path/to/file.cpp", Replace.GetFilePath());
+}
+
+TEST_F(ReplacementTest, ReturnsInvalidPath) {
+  Replacement Replace1(Context.Sources, SourceLocation(), 0, "");
+  EXPECT_EQ("invalid-location", Replace1.GetFilePath());
+
+  Replacement Replace2;
+  EXPECT_EQ("invalid-location", Replace2.GetFilePath());
+}
+
 TEST_F(ReplacementTest, CanApplyReplacements) {
   FileID ID = Context.CreateInMemoryFile("input.cpp",
                                          "line1\nline2\nline3\nline4");
@@ -227,5 +246,70 @@
             GetFileContentFromDisk("input.cpp"));
 }
 
+// FIXME: Copied from ToolingTest.cpp - put into a common header.
+namespace {
+class FindClassDeclXConsumer : public clang::ASTConsumer {
+ public:
+  FindClassDeclXConsumer(bool *FoundClassDeclX, StringRef ExpectedFile,
+                         unsigned ExpectedOffset, unsigned ExpectedLength)
+      : SM(NULL), FoundClassDeclX(FoundClassDeclX), ExpectedFile(ExpectedFile),
+        ExpectedOffset(ExpectedOffset), ExpectedLength(ExpectedLength) {}
+  virtual bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) {
+    if (CXXRecordDecl* Record = llvm::dyn_cast<clang::CXXRecordDecl>(
+            *GroupRef.begin())) {
+      if (Record->getName() == "X") {
+        Replacement Replace(*SM, Record, "");
+        EXPECT_EQ(ExpectedFile, Replace.GetFilePath());
+        EXPECT_EQ(ExpectedOffset, Replace.GetOffset());
+        EXPECT_EQ(ExpectedLength, Replace.GetLength());
+        *FoundClassDeclX = true;
+      }
+    }
+    return true;
+  }
+  clang::SourceManager *SM;
+ private:
+  bool *FoundClassDeclX;
+  std::string ExpectedFile;
+  unsigned ExpectedOffset;
+  unsigned ExpectedLength;
+};
+
+class TestAction : public clang::ASTFrontendAction {
+ public:
+  explicit TestAction(FindClassDeclXConsumer *TestConsumer)
+      : TestConsumer(TestConsumer) {}
+
+ protected:
+  virtual clang::ASTConsumer* CreateASTConsumer(
+      clang::CompilerInstance& compiler, llvm::StringRef dummy) {
+    TestConsumer->SM = &compiler.getSourceManager();
+    /// TestConsumer will be deleted by the framework calling us.
+    return TestConsumer;
+  }
+
+ private:
+  FindClassDeclXConsumer * const TestConsumer;
+};
+} // end namespace
+
+TEST(Replacement, CanBeConstructedFromNode) {
+  bool FoundClassDeclX = false;
+  EXPECT_TRUE(RunSyntaxOnlyToolOnCode(
+    new TestAction(
+      new FindClassDeclXConsumer(&FoundClassDeclX, "input.cc", 5, 7)),
+    "     class X;"));
+  EXPECT_TRUE(FoundClassDeclX);
+}
+
+TEST(Replacement, ReplacesAtSpellingLocation) {
+  bool FoundClassDeclX = false;
+  EXPECT_TRUE(RunSyntaxOnlyToolOnCode(
+    new TestAction(
+      new FindClassDeclXConsumer(&FoundClassDeclX, "input.cc", 17, 7)),
+    "#define A(Y) Y\nA(class X);"));
+  EXPECT_TRUE(FoundClassDeclX);
+}
+
 } // end namespace tooling
 } // end namespace clang





More information about the llvm-branch-commits mailing list