]> granicus.if.org Git - clang/commitdiff
[Rewrite] Extend to further accept CharSourceRange
authorJoel E. Denny <jdenny.ornl@gmail.com>
Sat, 6 Jul 2019 02:55:06 +0000 (02:55 +0000)
committerJoel E. Denny <jdenny.ornl@gmail.com>
Sat, 6 Jul 2019 02:55:06 +0000 (02:55 +0000)
Some Rewrite functions are already overloaded to accept
CharSourceRange, and this extends others in the same manner.  I'm
calling these in code that's not ready to upstream, but I figure they
might be useful to others in the meantime.

Reviewed By: jdoerfert

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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@365258 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Rewrite/Core/Rewriter.h
lib/Rewrite/Rewriter.cpp
unittests/Rewrite/CMakeLists.txt
unittests/Rewrite/RewriterTest.cpp [new file with mode: 0644]

index 5a3ff6c7984443388366f5dcffcbcd83867248a4..84c5ac3d72ee4720028ecb93cb0f7057542c7eb7 100644 (file)
@@ -84,7 +84,16 @@ public:
   /// in different buffers, this returns an empty string.
   ///
   /// Note that this method is not particularly efficient.
-  std::string getRewrittenText(SourceRange Range) const;
+  std::string getRewrittenText(CharSourceRange Range) const;
+
+  /// getRewrittenText - Return the rewritten form of the text in the specified
+  /// range.  If the start or end of the range was unrewritable or if they are
+  /// in different buffers, this returns an empty string.
+  ///
+  /// Note that this method is not particularly efficient.
+  std::string getRewrittenText(SourceRange Range) const {
+    return getRewrittenText(CharSourceRange::getTokenRange(Range));
+  }
 
   /// InsertText - Insert the specified string at the specified location in the
   /// original buffer.  This method returns true (and does nothing) if the input
@@ -138,6 +147,13 @@ public:
   bool ReplaceText(SourceLocation Start, unsigned OrigLength,
                    StringRef NewStr);
 
+  /// ReplaceText - This method replaces a range of characters in the input
+  /// buffer with a new string.  This is effectively a combined "remove/insert"
+  /// operation.
+  bool ReplaceText(CharSourceRange range, StringRef NewStr) {
+    return ReplaceText(range.getBegin(), getRangeSize(range), NewStr);
+  }
+
   /// ReplaceText - This method replaces a range of characters in the input
   /// buffer with a new string.  This is effectively a combined "remove/insert"
   /// operation.
index 281cf6f1be5e97962137d5e0cdc6dc0077ef00c2..881399e98e3397ea3142bfe6168c96dae1e49701 100644 (file)
@@ -170,7 +170,7 @@ int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const {
 /// in different buffers, this returns an empty string.
 ///
 /// Note that this method is not particularly efficient.
-std::string Rewriter::getRewrittenText(SourceRange Range) const {
+std::string Rewriter::getRewrittenText(CharSourceRange Range) const {
   if (!isRewritable(Range.getBegin()) ||
       !isRewritable(Range.getEnd()))
     return {};
@@ -193,7 +193,9 @@ std::string Rewriter::getRewrittenText(SourceRange Range) const {
 
     // Adjust the end offset to the end of the last token, instead of being the
     // start of the last token.
-    EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
+    if (Range.isTokenRange())
+      EndOff +=
+          Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
     return std::string(Ptr, Ptr+EndOff-StartOff);
   }
 
@@ -203,7 +205,8 @@ std::string Rewriter::getRewrittenText(SourceRange Range) const {
 
   // Adjust the end offset to the end of the last token, instead of being the
   // start of the last token.
-  EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
+  if (Range.isTokenRange())
+    EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
 
   // Advance the iterators to the right spot, yay for linear time algorithms.
   RewriteBuffer::iterator Start = RB.begin();
index a4c6e37245c607368fd1253908635e362922e983..613d716cb955014232afea407e5ccc3da1da5c14 100644 (file)
@@ -4,8 +4,10 @@ set(LLVM_LINK_COMPONENTS
 
 add_clang_unittest(RewriteTests
   RewriteBufferTest.cpp
+  RewriterTest.cpp
   )
 clang_target_link_libraries(RewriteTests
   PRIVATE
   clangRewrite
+  clangTooling
   )
diff --git a/unittests/Rewrite/RewriterTest.cpp b/unittests/Rewrite/RewriterTest.cpp
new file mode 100644 (file)
index 0000000..ca72dde
--- /dev/null
@@ -0,0 +1,80 @@
+//===- unittests/Rewrite/RewriterTest.cpp - Rewriter tests ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+
+namespace {
+
+struct RangeTypeTest {
+  std::unique_ptr<ASTUnit> AST;
+  Rewriter Rewrite;
+  SourceLocation FileStart;
+  CharSourceRange CRange; // covers exact char range
+  CharSourceRange TRange; // extends CRange to whole tokens
+  SourceRange SRange;     // different type but behaves like TRange
+  SourceLocation makeLoc(int Off) { return FileStart.getLocWithOffset(Off); }
+  CharSourceRange makeCharRange(int StartOff, int EndOff) {
+    return CharSourceRange::getCharRange(makeLoc(StartOff), makeLoc(EndOff));
+  }
+  RangeTypeTest(StringRef Code, int StartOff, int EndOff) {
+    AST = tooling::buildASTFromCode(Code);
+    ASTContext &C = AST->getASTContext();
+    Rewrite = Rewriter(C.getSourceManager(), C.getLangOpts());
+    FileStart = AST->getStartOfMainFileID();
+    CRange = makeCharRange(StartOff, EndOff);
+    SRange = CRange.getAsRange();
+    TRange = CharSourceRange::getTokenRange(SRange);
+  }
+};
+
+TEST(Rewriter, GetRewrittenTextRangeTypes) {
+  // Check that correct text is retrieved for each range type.  Check again
+  // after a modification.  Ranges remain in terms of the original text but
+  // include the new text.
+  StringRef Code = "int main() { return 0; }";
+  //              get char range ^~~    = "ret"
+  //             get token range ^~~+++ = "return"
+  //            get source range ^~~+++ = "return"
+  //                  insert "x" ^
+  //              get char range ^~~    = "xret"
+  //             get token range ^~~+++ = "xreturn"
+  //            get source range ^~~+++ = "xreturn"
+  RangeTypeTest T(Code, 13, 16);
+  EXPECT_EQ(T.Rewrite.getRewrittenText(T.CRange), "ret");
+  EXPECT_EQ(T.Rewrite.getRewrittenText(T.TRange), "return");
+  EXPECT_EQ(T.Rewrite.getRewrittenText(T.SRange), "return");
+  T.Rewrite.InsertText(T.makeLoc(13), "x");
+  EXPECT_EQ(T.Rewrite.getRewrittenText(T.CRange), "xret");
+  EXPECT_EQ(T.Rewrite.getRewrittenText(T.TRange), "xreturn");
+  EXPECT_EQ(T.Rewrite.getRewrittenText(T.SRange), "xreturn");
+}
+
+TEST(Rewriter, ReplaceTextRangeTypes) {
+  // Check that correct text is replaced for each range type.  Ranges remain in
+  // terms of the original text but include the new text.
+  StringRef Code = "int main(int argc, char *argv[]) { return argc; }";
+  //                            replace char range with "foo" ^~
+  //                                                      get ^~~~~ = "foogc;"
+  //                           replace token range with "bar" ^~++
+  //                                                      get ^~~~~ = "bar;"
+  //                            replace source range with "0" ^~++
+  //                                                      get ^~~~~ = "0;"
+  RangeTypeTest T(Code, 42, 44);
+  T.Rewrite.ReplaceText(T.CRange, "foo");
+  EXPECT_EQ(T.Rewrite.getRewrittenText(T.makeCharRange(42, 47)), "foogc;");
+  T.Rewrite.ReplaceText(T.TRange, "bar");
+  EXPECT_EQ(T.Rewrite.getRewrittenText(T.makeCharRange(42, 47)), "bar;");
+  T.Rewrite.ReplaceText(T.SRange, "0");
+  EXPECT_EQ(T.Rewrite.getRewrittenText(T.makeCharRange(42, 47)), "0;");
+}
+
+} // anonymous namespace