1*0d150db2SJacques Pienaar //===- unittests/Rewrite/RewriteBufferTest.cpp - RewriteBuffer tests ------===// 2*0d150db2SJacques Pienaar // 3*0d150db2SJacques Pienaar // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0d150db2SJacques Pienaar // See https://llvm.org/LICENSE.txt for license information. 5*0d150db2SJacques Pienaar // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0d150db2SJacques Pienaar // 7*0d150db2SJacques Pienaar //===----------------------------------------------------------------------===// 8*0d150db2SJacques Pienaar 9*0d150db2SJacques Pienaar #include "llvm/ADT/RewriteBuffer.h" 10*0d150db2SJacques Pienaar #include "gtest/gtest.h" 11*0d150db2SJacques Pienaar 12*0d150db2SJacques Pienaar using namespace llvm; 13*0d150db2SJacques Pienaar 14*0d150db2SJacques Pienaar namespace { 15*0d150db2SJacques Pienaar 16*0d150db2SJacques Pienaar #define EXPECT_OUTPUT(Buf, Output) EXPECT_EQ(Output, writeOutput(Buf)) 17*0d150db2SJacques Pienaar 18*0d150db2SJacques Pienaar static std::string writeOutput(const RewriteBuffer &Buf) { 19*0d150db2SJacques Pienaar std::string Result; 20*0d150db2SJacques Pienaar raw_string_ostream OS(Result); 21*0d150db2SJacques Pienaar Buf.write(OS); 22*0d150db2SJacques Pienaar return Result; 23*0d150db2SJacques Pienaar } 24*0d150db2SJacques Pienaar 25*0d150db2SJacques Pienaar static void tagRange(unsigned Offset, unsigned Len, StringRef tagName, 26*0d150db2SJacques Pienaar RewriteBuffer &Buf) { 27*0d150db2SJacques Pienaar std::string BeginTag; 28*0d150db2SJacques Pienaar raw_string_ostream(BeginTag) << '<' << tagName << '>'; 29*0d150db2SJacques Pienaar std::string EndTag; 30*0d150db2SJacques Pienaar raw_string_ostream(EndTag) << "</" << tagName << '>'; 31*0d150db2SJacques Pienaar 32*0d150db2SJacques Pienaar Buf.InsertTextAfter(Offset, BeginTag); 33*0d150db2SJacques Pienaar Buf.InsertTextBefore(Offset + Len, EndTag); 34*0d150db2SJacques Pienaar } 35*0d150db2SJacques Pienaar 36*0d150db2SJacques Pienaar TEST(RewriteBuffer, TagRanges) { 37*0d150db2SJacques Pienaar StringRef Input = "hello world"; 38*0d150db2SJacques Pienaar const char *Output = "<outer><inner>hello</inner></outer> "; 39*0d150db2SJacques Pienaar 40*0d150db2SJacques Pienaar RewriteBuffer Buf; 41*0d150db2SJacques Pienaar Buf.Initialize(Input); 42*0d150db2SJacques Pienaar StringRef RemoveStr = "world"; 43*0d150db2SJacques Pienaar size_t Pos = Input.find(RemoveStr); 44*0d150db2SJacques Pienaar Buf.RemoveText(Pos, RemoveStr.size()); 45*0d150db2SJacques Pienaar 46*0d150db2SJacques Pienaar StringRef TagStr = "hello"; 47*0d150db2SJacques Pienaar Pos = Input.find(TagStr); 48*0d150db2SJacques Pienaar tagRange(Pos, TagStr.size(), "outer", Buf); 49*0d150db2SJacques Pienaar tagRange(Pos, TagStr.size(), "inner", Buf); 50*0d150db2SJacques Pienaar 51*0d150db2SJacques Pienaar EXPECT_OUTPUT(Buf, Output); 52*0d150db2SJacques Pienaar } 53*0d150db2SJacques Pienaar 54*0d150db2SJacques Pienaar TEST(RewriteBuffer, DISABLED_RemoveLineIfEmpty_XFAIL) { 55*0d150db2SJacques Pienaar StringRef Input = "def\n" 56*0d150db2SJacques Pienaar "ghi\n" 57*0d150db2SJacques Pienaar "jkl\n"; 58*0d150db2SJacques Pienaar RewriteBuffer Buf; 59*0d150db2SJacques Pienaar Buf.Initialize(Input); 60*0d150db2SJacques Pienaar 61*0d150db2SJacques Pienaar // Insert "abc\n" at the start. 62*0d150db2SJacques Pienaar Buf.InsertText(0, "abc\n"); 63*0d150db2SJacques Pienaar EXPECT_OUTPUT(Buf, "abc\n" 64*0d150db2SJacques Pienaar "def\n" 65*0d150db2SJacques Pienaar "ghi\n" 66*0d150db2SJacques Pienaar "jkl\n"); 67*0d150db2SJacques Pienaar 68*0d150db2SJacques Pienaar // Remove "def\n". 69*0d150db2SJacques Pienaar // 70*0d150db2SJacques Pienaar // After the removal of "def", we have: 71*0d150db2SJacques Pienaar // 72*0d150db2SJacques Pienaar // "abc\n" 73*0d150db2SJacques Pienaar // "\n" 74*0d150db2SJacques Pienaar // "ghi\n" 75*0d150db2SJacques Pienaar // "jkl\n" 76*0d150db2SJacques Pienaar // 77*0d150db2SJacques Pienaar // Because removeLineIfEmpty=true, RemoveText has to remove the "\n" left on 78*0d150db2SJacques Pienaar // the line. This happens correctly for the rewrite buffer itself, so the 79*0d150db2SJacques Pienaar // next check below passes. 80*0d150db2SJacques Pienaar // 81*0d150db2SJacques Pienaar // However, RemoveText's implementation incorrectly records the delta for 82*0d150db2SJacques Pienaar // removing the "\n" using the rewrite buffer offset, 4, where it was 83*0d150db2SJacques Pienaar // supposed to use the original input offset, 3. Interpreted as an original 84*0d150db2SJacques Pienaar // input offset, 4 points to "g" not to "\n". Thus, any future modifications 85*0d150db2SJacques Pienaar // at the original input's "g" will incorrectly see "g" as having become an 86*0d150db2SJacques Pienaar // empty string and so will map to the next character, "h", in the rewrite 87*0d150db2SJacques Pienaar // buffer. 88*0d150db2SJacques Pienaar StringRef RemoveStr0 = "def"; 89*0d150db2SJacques Pienaar Buf.RemoveText(Input.find(RemoveStr0), RemoveStr0.size(), 90*0d150db2SJacques Pienaar /*removeLineIfEmpty*/ true); 91*0d150db2SJacques Pienaar EXPECT_OUTPUT(Buf, "abc\n" 92*0d150db2SJacques Pienaar "ghi\n" 93*0d150db2SJacques Pienaar "jkl\n"); 94*0d150db2SJacques Pienaar 95*0d150db2SJacques Pienaar // Try to remove "ghi\n". 96*0d150db2SJacques Pienaar // 97*0d150db2SJacques Pienaar // As discussed above, the original input offset for "ghi\n" incorrectly 98*0d150db2SJacques Pienaar // maps to the rewrite buffer offset for "hi\nj", so we end up with: 99*0d150db2SJacques Pienaar // 100*0d150db2SJacques Pienaar // "abc\n" 101*0d150db2SJacques Pienaar // "gkl\n" 102*0d150db2SJacques Pienaar // 103*0d150db2SJacques Pienaar // To show that removeLineIfEmpty=true is the culprit, change true to false 104*0d150db2SJacques Pienaar // and append a newline to RemoveStr0 above. The test then passes. 105*0d150db2SJacques Pienaar StringRef RemoveStr1 = "ghi\n"; 106*0d150db2SJacques Pienaar Buf.RemoveText(Input.find(RemoveStr1), RemoveStr1.size()); 107*0d150db2SJacques Pienaar EXPECT_OUTPUT(Buf, "abc\n" 108*0d150db2SJacques Pienaar "jkl\n"); 109*0d150db2SJacques Pienaar } 110*0d150db2SJacques Pienaar 111*0d150db2SJacques Pienaar } // anonymous namespace 112