r315918 - Recommit r315738 "[clang-refactor] Apply source replacements"

Alex Lorenz via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 16 10:31:16 PDT 2017


Author: arphaman
Date: Mon Oct 16 10:31:16 2017
New Revision: 315918

URL: http://llvm.org/viewvc/llvm-project?rev=315918&view=rev
Log:
Recommit r315738 "[clang-refactor] Apply source replacements"

The fixed commit ensures that ParsedSourceRange works correctly
with Windows paths.

Original message:

This commit actually brings clang-refactor to a usable state as it can now
apply the refactoring changes to source files.
The -selection option is now also fully supported.

Differential Revision: https://reviews.llvm.org/D38402

Added:
    cfe/trunk/test/Refactor/tool-apply-replacements.cpp
    cfe/trunk/test/Refactor/tool-selection-option.c
    cfe/trunk/unittests/Frontend/ParsedSourceLocationTest.cpp
Modified:
    cfe/trunk/include/clang/Frontend/CommandLineSourceLoc.h
    cfe/trunk/tools/clang-refactor/ClangRefactor.cpp
    cfe/trunk/unittests/Frontend/CMakeLists.txt

Modified: cfe/trunk/include/clang/Frontend/CommandLineSourceLoc.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/CommandLineSourceLoc.h?rev=315918&r1=315917&r2=315918&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/CommandLineSourceLoc.h (original)
+++ cfe/trunk/include/clang/Frontend/CommandLineSourceLoc.h Mon Oct 16 10:31:16 2017
@@ -51,6 +51,52 @@ public:
   }
 };
 
+/// A source range that has been parsed on the command line.
+struct ParsedSourceRange {
+  std::string FileName;
+  /// The starting location of the range. The first element is the line and
+  /// the second element is the column.
+  std::pair<unsigned, unsigned> Begin;
+  /// The ending location of the range. The first element is the line and the
+  /// second element is the column.
+  std::pair<unsigned, unsigned> End;
+
+  /// Returns a parsed source range from a string or None if the string is
+  /// invalid.
+  ///
+  /// These source string has the following format:
+  ///
+  /// file:start_line:start_column[-end_line:end_column]
+  ///
+  /// If the end line and column are omitted, the starting line and columns
+  /// are used as the end values.
+  static Optional<ParsedSourceRange> fromString(StringRef Str) {
+    std::pair<StringRef, StringRef> RangeSplit = Str.rsplit('-');
+    unsigned EndLine, EndColumn;
+    bool HasEndLoc = false;
+    if (!RangeSplit.second.empty()) {
+      std::pair<StringRef, StringRef> Split = RangeSplit.second.rsplit(':');
+      if (Split.first.getAsInteger(10, EndLine) ||
+          Split.second.getAsInteger(10, EndColumn)) {
+        // The string does not end in end_line:end_column, so the '-'
+        // probably belongs to the filename which menas the whole
+        // string should be parsed.
+        RangeSplit.first = Str;
+      } else
+        HasEndLoc = true;
+    }
+    auto Begin = ParsedSourceLocation::FromString(RangeSplit.first);
+    if (Begin.FileName.empty())
+      return None;
+    if (!HasEndLoc) {
+      EndLine = Begin.Line;
+      EndColumn = Begin.Column;
+    }
+    return ParsedSourceRange{std::move(Begin.FileName),
+                             {Begin.Line, Begin.Column},
+                             {EndLine, EndColumn}};
+  }
+};
 }
 
 namespace llvm {

Added: cfe/trunk/test/Refactor/tool-apply-replacements.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Refactor/tool-apply-replacements.cpp?rev=315918&view=auto
==============================================================================
--- cfe/trunk/test/Refactor/tool-apply-replacements.cpp (added)
+++ cfe/trunk/test/Refactor/tool-apply-replacements.cpp Mon Oct 16 10:31:16 2017
@@ -0,0 +1,11 @@
+// RUN: rm -f %t.cp.cpp
+// RUN: cp %s %t.cp.cpp
+// RUN: clang-refactor local-rename -selection=%t.cp.cpp:9:7 -new-name=test %t.cp.cpp --
+// RUN: grep -v CHECK %t.cp.cpp | FileCheck %t.cp.cpp
+// RUN: cp %s %t.cp.cpp
+// RUN: clang-refactor local-rename -selection=%t.cp.cpp:9:7-9:15 -new-name=test %t.cp.cpp --
+// RUN: grep -v CHECK %t.cp.cpp | FileCheck %t.cp.cpp
+
+class RenameMe {
+// CHECK: class test {
+};

Added: cfe/trunk/test/Refactor/tool-selection-option.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Refactor/tool-selection-option.c?rev=315918&view=auto
==============================================================================
--- cfe/trunk/test/Refactor/tool-selection-option.c (added)
+++ cfe/trunk/test/Refactor/tool-selection-option.c Mon Oct 16 10:31:16 2017
@@ -0,0 +1,15 @@
+// RUN: rm -f %t.cp.c
+// RUN: cp %s %t.cp.c
+// RUN: clang-refactor local-rename -selection=%t.cp.c:6:5 -new-name=test -v %t.cp.c -- | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor local-rename -selection=%t.cp.c:6:5-6:9 -new-name=test -v %t.cp.c -- | FileCheck --check-prefix=CHECK2 %s
+
+int test;
+
+// CHECK1: invoking action 'local-rename':
+// CHECK1-NEXT: -selection={{.*}}.cp.c:6:5 -> {{.*}}.cp.c:6:5
+
+// CHECK2: invoking action 'local-rename':
+// CHECK2-NEXT: -selection={{.*}}.cp.c:6:5 -> {{.*}}.cp.c:6:9
+
+// RUN: not clang-refactor local-rename -selection=%s:6:5 -new-name=test -v %t.cp.c -- 2>&1 | FileCheck --check-prefix=CHECK-FILE-ERR %s
+// CHECK-FILE-ERR: given file is not in the target TU

Modified: cfe/trunk/tools/clang-refactor/ClangRefactor.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-refactor/ClangRefactor.cpp?rev=315918&r1=315917&r2=315918&view=diff
==============================================================================
--- cfe/trunk/tools/clang-refactor/ClangRefactor.cpp (original)
+++ cfe/trunk/tools/clang-refactor/ClangRefactor.cpp Mon Oct 16 10:31:16 2017
@@ -14,6 +14,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "TestSupport.h"
+#include "clang/Frontend/CommandLineSourceLoc.h"
 #include "clang/Rewrite/Core/Rewriter.h"
 #include "clang/Tooling/CommonOptionsParser.h"
 #include "clang/Tooling/Refactoring.h"
@@ -54,7 +55,7 @@ public:
 
   /// Prints any additional state associated with the selection argument to
   /// the given output stream.
-  virtual void print(raw_ostream &OS) = 0;
+  virtual void print(raw_ostream &OS) {}
 
   /// Returns a replacement refactoring result consumer (if any) that should
   /// consume the results of a refactoring operation.
@@ -99,6 +100,41 @@ private:
   TestSelectionRangesInFile TestSelections;
 };
 
+/// Stores the parsed -selection=filename:line:column[-line:column] option.
+class SourceRangeSelectionArgument final : public SourceSelectionArgument {
+public:
+  SourceRangeSelectionArgument(ParsedSourceRange Range)
+      : Range(std::move(Range)) {}
+
+  bool forAllRanges(const SourceManager &SM,
+                    llvm::function_ref<void(SourceRange R)> Callback) override {
+    const FileEntry *FE = SM.getFileManager().getFile(Range.FileName);
+    FileID FID = FE ? SM.translateFile(FE) : FileID();
+    if (!FE || FID.isInvalid()) {
+      llvm::errs() << "error: -selection=" << Range.FileName
+                   << ":... : given file is not in the target TU\n";
+      return true;
+    }
+
+    SourceLocation Start = SM.getMacroArgExpandedLocation(
+        SM.translateLineCol(FID, Range.Begin.first, Range.Begin.second));
+    SourceLocation End = SM.getMacroArgExpandedLocation(
+        SM.translateLineCol(FID, Range.End.first, Range.End.second));
+    if (Start.isInvalid() || End.isInvalid()) {
+      llvm::errs() << "error: -selection=" << Range.FileName << ':'
+                   << Range.Begin.first << ':' << Range.Begin.second << '-'
+                   << Range.End.first << ':' << Range.End.second
+                   << " : invalid source location\n";
+      return true;
+    }
+    Callback(SourceRange(Start, End));
+    return false;
+  }
+
+private:
+  ParsedSourceRange Range;
+};
+
 std::unique_ptr<SourceSelectionArgument>
 SourceSelectionArgument::fromString(StringRef Value) {
   if (Value.startswith("test:")) {
@@ -110,10 +146,12 @@ SourceSelectionArgument::fromString(Stri
     return llvm::make_unique<TestSourceSelectionArgument>(
         std::move(*ParsedTestSelection));
   }
-  // FIXME: Support true selection ranges.
+  Optional<ParsedSourceRange> Range = ParsedSourceRange::fromString(Value);
+  if (Range)
+    return llvm::make_unique<SourceRangeSelectionArgument>(std::move(*Range));
   llvm::errs() << "error: '-selection' option must be specified using "
                   "<file>:<line>:<column> or "
-                  "<file>:<line>:<column>-<line>:<column> format";
+                  "<file>:<line>:<column>-<line>:<column> format\n";
   return nullptr;
 }
 
@@ -268,11 +306,22 @@ private:
 
 class ClangRefactorConsumer : public RefactoringResultConsumer {
 public:
-  void handleError(llvm::Error Err) {
+  void handleError(llvm::Error Err) override {
     llvm::errs() << llvm::toString(std::move(Err)) << "\n";
   }
 
-  // FIXME: Consume atomic changes and apply them to files.
+  void handle(AtomicChanges Changes) override {
+    SourceChanges.insert(SourceChanges.begin(), Changes.begin(), Changes.end());
+  }
+
+  void handle(SymbolOccurrences Occurrences) override {
+    RefactoringResultConsumer::handle(std::move(Occurrences));
+  }
+
+  const AtomicChanges &getSourceChanges() const { return SourceChanges; }
+
+private:
+  AtomicChanges SourceChanges;
 };
 
 class ClangRefactorTool {
@@ -352,6 +401,39 @@ public:
     }
   }
 
+  bool applySourceChanges(const AtomicChanges &Replacements) {
+    std::set<std::string> Files;
+    for (const auto &Change : Replacements)
+      Files.insert(Change.getFilePath());
+    // FIXME: Add automatic formatting support as well.
+    tooling::ApplyChangesSpec Spec;
+    // FIXME: We should probably cleanup the result by default as well.
+    Spec.Cleanup = false;
+    for (const auto &File : Files) {
+      llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr =
+          llvm::MemoryBuffer::getFile(File);
+      if (!BufferErr) {
+        llvm::errs() << "error: failed to open " << File << " for rewriting\n";
+        return true;
+      }
+      auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(),
+                                                Replacements, Spec);
+      if (!Result) {
+        llvm::errs() << toString(Result.takeError());
+        return true;
+      }
+
+      std::error_code EC;
+      llvm::raw_fd_ostream OS(File, EC, llvm::sys::fs::F_Text);
+      if (EC) {
+        llvm::errs() << EC.message() << "\n";
+        return true;
+      }
+      OS << *Result;
+    }
+    return false;
+  }
+
   bool invokeAction(RefactoringActionSubcommand &Subcommand,
                     const CompilationDatabase &DB,
                     ArrayRef<std::string> Sources) {
@@ -423,7 +505,7 @@ public:
           // FIXME (Alex L): Implement non-selection based invocation path.
         }))
       return true;
-    return HasFailed;
+    return HasFailed || applySourceChanges(Consumer.getSourceChanges());
   }
 };
 

Modified: cfe/trunk/unittests/Frontend/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Frontend/CMakeLists.txt?rev=315918&r1=315917&r2=315918&view=diff
==============================================================================
--- cfe/trunk/unittests/Frontend/CMakeLists.txt (original)
+++ cfe/trunk/unittests/Frontend/CMakeLists.txt Mon Oct 16 10:31:16 2017
@@ -7,6 +7,7 @@ add_clang_unittest(FrontendTests
   CompilerInstanceTest.cpp
   FrontendActionTest.cpp
   CodeGenActionTest.cpp
+  ParsedSourceLocationTest.cpp
   PCHPreambleTest.cpp
   )
 target_link_libraries(FrontendTests

Added: cfe/trunk/unittests/Frontend/ParsedSourceLocationTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Frontend/ParsedSourceLocationTest.cpp?rev=315918&view=auto
==============================================================================
--- cfe/trunk/unittests/Frontend/ParsedSourceLocationTest.cpp (added)
+++ cfe/trunk/unittests/Frontend/ParsedSourceLocationTest.cpp Mon Oct 16 10:31:16 2017
@@ -0,0 +1,37 @@
+//===- unittests/Frontend/ParsedSourceLocationTest.cpp - ------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Frontend/CommandLineSourceLoc.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace {
+
+TEST(ParsedSourceRange, ParseTest) {
+  auto Check = [](StringRef Value, StringRef Filename, unsigned BeginLine,
+                  unsigned BeginColumn, unsigned EndLine, unsigned EndColumn) {
+    Optional<ParsedSourceRange> PSR = ParsedSourceRange::fromString(Value);
+    ASSERT_TRUE(PSR);
+    EXPECT_EQ(PSR->FileName, Filename);
+    EXPECT_EQ(PSR->Begin.first, BeginLine);
+    EXPECT_EQ(PSR->Begin.second, BeginColumn);
+    EXPECT_EQ(PSR->End.first, EndLine);
+    EXPECT_EQ(PSR->End.second, EndColumn);
+  };
+
+  Check("/Users/test/a-b.cpp:1:2", "/Users/test/a-b.cpp", 1, 2, 1, 2);
+  Check("/Users/test/a-b.cpp:1:2-3:4", "/Users/test/a-b.cpp", 1, 2, 3, 4);
+
+  Check("C:/Users/bob/a-b.cpp:1:2", "C:/Users/bob/a-b.cpp", 1, 2, 1, 2);
+  Check("C:/Users/bob/a-b.cpp:1:2-3:4", "C:/Users/bob/a-b.cpp", 1, 2, 3, 4);
+}
+
+} // anonymous namespace




More information about the cfe-commits mailing list