xref: /llvm-project/clang/unittests/Format/CleanupTest.cpp (revision 1ef68456cff73736256b923b0b0cd2cfd7f8ce43)
1 //===- unittest/Format/CleanupTest.cpp - Code cleanup unit tests ----------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "clang/Format/Format.h"
11 
12 #include "../Tooling/RewriterTestContext.h"
13 #include "clang/Tooling/Core/Replacement.h"
14 
15 #include "gtest/gtest.h"
16 
17 namespace clang {
18 namespace format {
19 namespace {
20 
21 class CleanupTest : public ::testing::Test {
22 protected:
23   std::string cleanup(llvm::StringRef Code,
24                       const std::vector<tooling::Range> &Ranges,
25                       const FormatStyle &Style = getLLVMStyle()) {
26     tooling::Replacements Replaces = format::cleanup(Style, Code, Ranges);
27 
28     std::string Result = applyAllReplacements(Code, Replaces);
29     EXPECT_NE("", Result);
30     return Result;
31   }
32 };
33 
34 TEST_F(CleanupTest, DeleteEmptyNamespaces) {
35   std::string Code = "namespace A {\n"
36                      "namespace B {\n"
37                      "} // namespace B\n"
38                      "} // namespace A\n\n"
39                      "namespace C {\n"
40                      "namespace D { int i; }\n"
41                      "inline namespace E { namespace { } }\n"
42                      "}";
43   std::string Expected = "\n\n\n\n\nnamespace C {\n"
44                          "namespace D { int i; }\n   \n"
45                          "}";
46   std::vector<tooling::Range> Ranges;
47   Ranges.push_back(tooling::Range(28, 0));
48   Ranges.push_back(tooling::Range(91, 6));
49   Ranges.push_back(tooling::Range(132, 0));
50   std::string Result = cleanup(Code, Ranges);
51   EXPECT_EQ(Expected, Result);
52 }
53 
54 TEST_F(CleanupTest, NamespaceWithSyntaxError) {
55   std::string Code = "namespace A {\n"
56                      "namespace B {\n" // missing r_brace
57                      "} // namespace A\n\n"
58                      "namespace C {\n"
59                      "namespace D int i; }\n"
60                      "inline namespace E { namespace { } }\n"
61                      "}";
62   std::string Expected = "namespace A {\n"
63                          "\n\n\nnamespace C {\n"
64                          "namespace D int i; }\n   \n"
65                          "}";
66   std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
67   std::string Result = cleanup(Code, Ranges);
68   EXPECT_EQ(Expected, Result);
69 }
70 
71 TEST_F(CleanupTest, EmptyNamespaceNotAffected) {
72   std::string Code = "namespace A {\n\n"
73                      "namespace {\n\n}}";
74   // Even though the namespaces are empty, but the inner most empty namespace
75   // block is not affected by the changed ranges.
76   std::string Expected = "namespace A {\n\n"
77                          "namespace {\n\n}}";
78   // Set the changed range to be the second "\n".
79   std::vector<tooling::Range> Ranges(1, tooling::Range(14, 0));
80   std::string Result = cleanup(Code, Ranges);
81   EXPECT_EQ(Expected, Result);
82 }
83 
84 TEST_F(CleanupTest, EmptyNamespaceWithCommentsNoBreakBeforeBrace) {
85   std::string Code = "namespace A {\n"
86                      "namespace B {\n"
87                      "// Yo\n"
88                      "} // namespace B\n"
89                      "} // namespace A\n"
90                      "namespace C { // Yo\n"
91                      "}";
92   std::string Expected = "\n\n\n\n\n\n";
93   std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
94   std::string Result = cleanup(Code, Ranges);
95   EXPECT_EQ(Expected, Result);
96 }
97 
98 TEST_F(CleanupTest, EmptyNamespaceWithCommentsBreakBeforeBrace) {
99   std::string Code = "namespace A\n"
100                      "/* Yo */ {\n"
101                      "namespace B\n"
102                      "{\n"
103                      "// Yo\n"
104                      "} // namespace B\n"
105                      "} // namespace A\n"
106                      "namespace C\n"
107                      "{ // Yo\n"
108                      "}\n";
109   std::string Expected = "\n\n\n\n\n\n\n\n\n\n";
110   std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
111   FormatStyle Style = getLLVMStyle();
112   Style.BraceWrapping.AfterNamespace = true;
113   std::string Result = cleanup(Code, Ranges, Style);
114   EXPECT_EQ(Expected, Result);
115 }
116 
117 TEST_F(CleanupTest, CtorInitializationSimpleRedundantComma) {
118   std::string Code = "class A {\nA() : , {} };";
119   std::string Expected = "class A {\nA()  {} };";
120   std::vector<tooling::Range> Ranges;
121   Ranges.push_back(tooling::Range(17, 0));
122   Ranges.push_back(tooling::Range(19, 0));
123   std::string Result = cleanup(Code, Ranges);
124   EXPECT_EQ(Expected, Result);
125 
126   Code = "class A {\nA() : x(1), {} };";
127   Expected = "class A {\nA() : x(1) {} };";
128   Ranges.clear();
129   Ranges.push_back(tooling::Range(23, 0));
130   Result = cleanup(Code, Ranges);
131   EXPECT_EQ(Expected, Result);
132 
133   Code = "class A {\nA() :,,,,{} };";
134   Expected = "class A {\nA() {} };";
135   Ranges.clear();
136   Ranges.push_back(tooling::Range(15, 0));
137   Result = cleanup(Code, Ranges);
138   EXPECT_EQ(Expected, Result);
139 }
140 
141 TEST_F(CleanupTest, ListSimpleRedundantComma) {
142   std::string Code = "void f() { std::vector<int> v = {1,2,,,3,{4,5}}; }";
143   std::string Expected = "void f() { std::vector<int> v = {1,2,3,{4,5}}; }";
144   std::vector<tooling::Range> Ranges;
145   Ranges.push_back(tooling::Range(40, 0));
146   std::string Result = cleanup(Code, Ranges);
147   EXPECT_EQ(Expected, Result);
148 
149   Code = "int main() { f(1,,2,3,,4);}";
150   Expected = "int main() { f(1,2,3,4);}";
151   Ranges.clear();
152   Ranges.push_back(tooling::Range(17, 0));
153   Ranges.push_back(tooling::Range(22, 0));
154   Result = cleanup(Code, Ranges);
155   EXPECT_EQ(Expected, Result);
156 }
157 
158 TEST_F(CleanupTest, CtorInitializationBracesInParens) {
159   std::string Code = "class A {\nA() : x({1}),, {} };";
160   std::string Expected = "class A {\nA() : x({1}) {} };";
161   std::vector<tooling::Range> Ranges;
162   Ranges.push_back(tooling::Range(24, 0));
163   Ranges.push_back(tooling::Range(26, 0));
164   std::string Result = cleanup(Code, Ranges);
165   EXPECT_EQ(Expected, Result);
166 }
167 
168 TEST_F(CleanupTest, RedundantCommaNotInAffectedRanges) {
169   std::string Code =
170       "class A {\nA() : x({1}), /* comment */, { int x = 0; } };";
171   std::string Expected =
172       "class A {\nA() : x({1}), /* comment */, { int x = 0; } };";
173   // Set the affected range to be "int x = 0", which does not intercept the
174   // constructor initialization list.
175   std::vector<tooling::Range> Ranges(1, tooling::Range(42, 9));
176   std::string Result = cleanup(Code, Ranges);
177   EXPECT_EQ(Expected, Result);
178 
179   Code = "class A {\nA() : x(1), {} };";
180   Expected = "class A {\nA() : x(1), {} };";
181   // No range. Fixer should do nothing.
182   Ranges.clear();
183   Result = cleanup(Code, Ranges);
184   EXPECT_EQ(Expected, Result);
185 }
186 
187 // FIXME: delete comments too.
188 TEST_F(CleanupTest, CtorInitializationCommentAroundCommas) {
189   // Remove redundant commas around comment.
190   std::string Code = "class A {\nA() : x({1}), /* comment */, {} };";
191   std::string Expected = "class A {\nA() : x({1}) /* comment */ {} };";
192   std::vector<tooling::Range> Ranges;
193   Ranges.push_back(tooling::Range(25, 0));
194   Ranges.push_back(tooling::Range(40, 0));
195   std::string Result = cleanup(Code, Ranges);
196   EXPECT_EQ(Expected, Result);
197 
198   // Remove trailing comma and ignore comment.
199   Code = "class A {\nA() : x({1}), // comment\n{} };";
200   Expected = "class A {\nA() : x({1}) // comment\n{} };";
201   Ranges = std::vector<tooling::Range>(1, tooling::Range(25, 0));
202   Result = cleanup(Code, Ranges);
203   EXPECT_EQ(Expected, Result);
204 
205   // Remove trailing comma and ignore comment.
206   Code = "class A {\nA() : x({1}), // comment\n , y(1),{} };";
207   Expected = "class A {\nA() : x({1}), // comment\n  y(1){} };";
208   Ranges = std::vector<tooling::Range>(1, tooling::Range(38, 0));
209   Result = cleanup(Code, Ranges);
210   EXPECT_EQ(Expected, Result);
211 
212   // Remove trailing comma and ignore comment.
213   Code = "class A {\nA() : x({1}), \n/* comment */, y(1),{} };";
214   Expected = "class A {\nA() : x({1}), \n/* comment */ y(1){} };";
215   Ranges = std::vector<tooling::Range>(1, tooling::Range(40, 0));
216   Result = cleanup(Code, Ranges);
217   EXPECT_EQ(Expected, Result);
218 
219   // Remove trailing comma and ignore comment.
220   Code = "class A {\nA() : , // comment\n y(1),{} };";
221   Expected = "class A {\nA() :  // comment\n y(1){} };";
222   Ranges = std::vector<tooling::Range>(1, tooling::Range(17, 0));
223   Result = cleanup(Code, Ranges);
224   EXPECT_EQ(Expected, Result);
225 }
226 
227 TEST_F(CleanupTest, CtorInitializerInNamespace) {
228   std::string Code = "namespace A {\n"
229                      "namespace B {\n" // missing r_brace
230                      "} // namespace A\n\n"
231                      "namespace C {\n"
232                      "class A { A() : x(0),, {} };\n"
233                      "inline namespace E { namespace { } }\n"
234                      "}";
235   std::string Expected = "namespace A {\n"
236                          "\n\n\nnamespace C {\n"
237                          "class A { A() : x(0) {} };\n   \n"
238                          "}";
239   std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
240   std::string Result = cleanup(Code, Ranges);
241   EXPECT_EQ(Expected, Result);
242 }
243 
244 class CleanUpReplacementsTest : public ::testing::Test {
245 protected:
246   tooling::Replacement createReplacement(SourceLocation Start, unsigned Length,
247                                          llvm::StringRef ReplacementText) {
248     return tooling::Replacement(Context.Sources, Start, Length,
249                                 ReplacementText);
250   }
251 
252   RewriterTestContext Context;
253 };
254 
255 TEST_F(CleanUpReplacementsTest, FixOnlyAffectedCodeAfterReplacements) {
256   std::string Code = "namespace A {\n"
257                      "namespace B {\n"
258                      "  int x;\n"
259                      "} // namespace B\n"
260                      "} // namespace A\n"
261                      "\n"
262                      "namespace C {\n"
263                      "namespace D { int i; }\n"
264                      "inline namespace E { namespace { int y; } }\n"
265                      "int x=     0;"
266                      "}";
267   std::string Expected = "\n\nnamespace C {\n"
268                          "namespace D { int i; }\n\n"
269                          "int x=     0;"
270                          "}";
271   FileID ID = Context.createInMemoryFile("fix.cpp", Code);
272   tooling::Replacements Replaces;
273   Replaces.insert(tooling::Replacement(Context.Sources,
274                                        Context.getLocation(ID, 3, 3), 6, ""));
275   Replaces.insert(tooling::Replacement(Context.Sources,
276                                        Context.getLocation(ID, 9, 34), 6, ""));
277 
278   format::FormatStyle Style = format::getLLVMStyle();
279   auto FinalReplaces = formatReplacements(
280       Code, cleanupAroundReplacements(Code, Replaces, Style), Style);
281   EXPECT_EQ(Expected, applyAllReplacements(Code, FinalReplaces));
282 }
283 
284 } // end namespace
285 } // end namespace format
286 } // end namespace clang
287