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