1 //===- unittest/Format/CleanupTest.cpp - Code cleanup unit 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/Format/Format.h"
10 
11 #include "../Tooling/ReplacementTest.h"
12 #include "../Tooling/RewriterTestContext.h"
13 #include "clang/Tooling/Core/Replacement.h"
14 
15 #include "gtest/gtest.h"
16 
17 using clang::tooling::ReplacementTest;
18 using clang::tooling::toReplacements;
19 
20 namespace clang {
21 namespace format {
22 namespace {
23 
24 class CleanupTest : public ::testing::Test {
25 protected:
26   std::string cleanup(llvm::StringRef Code,
27                       const std::vector<tooling::Range> &Ranges,
28                       const FormatStyle &Style = getLLVMStyle()) {
29     tooling::Replacements Replaces = format::cleanup(Style, Code, Ranges);
30 
31     auto Result = applyAllReplacements(Code, Replaces);
32     EXPECT_TRUE(static_cast<bool>(Result));
33     return *Result;
34   }
35 
36   // Returns code after cleanup around \p Offsets.
37   std::string cleanupAroundOffsets(llvm::ArrayRef<unsigned> Offsets,
38                                    llvm::StringRef Code,
39                                    const FormatStyle &Style = getLLVMStyle()) {
40     std::vector<tooling::Range> Ranges;
41     for (auto Offset : Offsets)
42       Ranges.push_back(tooling::Range(Offset, 0));
43     return cleanup(Code, Ranges, Style);
44   }
45 };
46 
47 TEST_F(CleanupTest, DeleteEmptyNamespaces) {
48   std::string Code = "namespace A {\n"
49                      "namespace B {\n"
50                      "} // namespace B\n"
51                      "} // namespace A\n\n"
52                      "namespace C {\n"
53                      "namespace D { int i; }\n"
54                      "inline namespace E { namespace { } }\n"
55                      "}";
56   std::string Expected = "\n\n\n\n\nnamespace C {\n"
57                          "namespace D { int i; }\n   \n"
58                          "}";
59   EXPECT_EQ(Expected, cleanupAroundOffsets({28, 91, 132}, Code));
60 }
61 
62 TEST_F(CleanupTest, NamespaceWithSyntaxError) {
63   std::string Code = "namespace A {\n"
64                      "namespace B {\n" // missing r_brace
65                      "} // namespace A\n\n"
66                      "namespace C {\n"
67                      "namespace D int i; }\n"
68                      "inline namespace E { namespace { } }\n"
69                      "}";
70   std::string Expected = "namespace A {\n"
71                          "\n\n\nnamespace C {\n"
72                          "namespace D int i; }\n   \n"
73                          "}";
74   std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
75   EXPECT_EQ(Expected, cleanup(Code, Ranges));
76 }
77 
78 TEST_F(CleanupTest, EmptyNamespaceNotAffected) {
79   std::string Code = "namespace A {\n\n"
80                      "namespace {\n\n}}";
81   // Even though the namespaces are empty, but the inner most empty namespace
82   // block is not affected by the changed ranges.
83   std::string Expected = "namespace A {\n\n"
84                          "namespace {\n\n}}";
85   // Set the changed range to be the second "\n".
86   EXPECT_EQ(Expected, cleanupAroundOffsets({14}, Code));
87 }
88 
89 TEST_F(CleanupTest, EmptyNamespaceWithCommentsNoBreakBeforeBrace) {
90   std::string Code = "namespace A {\n"
91                      "namespace B {\n"
92                      "// Yo\n"
93                      "} // namespace B\n"
94                      "} // namespace A\n"
95                      "namespace C { // Yo\n"
96                      "}";
97   std::string Expected = "\n\n\n\n\n\n";
98   std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
99   std::string Result = cleanup(Code, Ranges);
100   EXPECT_EQ(Expected, Result);
101 }
102 
103 TEST_F(CleanupTest, EmptyNamespaceWithCommentsBreakBeforeBrace) {
104   std::string Code = "namespace A\n"
105                      "/* Yo */ {\n"
106                      "namespace B\n"
107                      "{\n"
108                      "// Yo\n"
109                      "} // namespace B\n"
110                      "} // namespace A\n"
111                      "namespace C\n"
112                      "{ // Yo\n"
113                      "}\n";
114   std::string Expected = "\n\n\n\n\n\n\n\n\n\n";
115   std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
116   FormatStyle Style = getLLVMStyle();
117   Style.BraceWrapping.AfterNamespace = true;
118   std::string Result = cleanup(Code, Ranges, Style);
119   EXPECT_EQ(Expected, Result);
120 }
121 
122 TEST_F(CleanupTest, EmptyNamespaceAroundConditionalCompilation) {
123   std::string Code = "#ifdef A\n"
124                      "int a;\n"
125                      "int b;\n"
126                      "#else\n"
127                      "#endif\n"
128                      "namespace {}";
129   std::string Expected = "#ifdef A\n"
130                          "int a;\n"
131                          "int b;\n"
132                          "#else\n"
133                          "#endif\n";
134   std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
135   FormatStyle Style = getLLVMStyle();
136   std::string Result = cleanup(Code, Ranges, Style);
137   EXPECT_EQ(Expected, Result);
138 }
139 
140 TEST_F(CleanupTest, CtorInitializationSimpleRedundantComma) {
141   std::string Code = "class A {\nA() : , {} };";
142   std::string Expected = "class A {\nA()  {} };";
143   EXPECT_EQ(Expected, cleanupAroundOffsets({17, 19}, Code));
144 
145   Code = "class A {\nA() : x(1), {} };";
146   Expected = "class A {\nA() : x(1) {} };";
147   EXPECT_EQ(Expected, cleanupAroundOffsets({23}, Code));
148 
149   Code = "class A {\nA() :,,,,{} };";
150   Expected = "class A {\nA() {} };";
151   EXPECT_EQ(Expected, cleanupAroundOffsets({15}, Code));
152 }
153 
154 TEST_F(CleanupTest, CtorInitializationSimpleRedundantColon) {
155   std::string Code = "class A {\nA() : =default; };";
156   std::string Expected = "class A {\nA()  =default; };";
157   EXPECT_EQ(Expected, cleanupAroundOffsets({15}, Code));
158 
159   Code = "class A {\nA() : , =default; };";
160   Expected = "class A {\nA()  =default; };";
161   EXPECT_EQ(Expected, cleanupAroundOffsets({15}, Code));
162 }
163 
164 TEST_F(CleanupTest, ListRedundantComma) {
165   std::string Code = "void f() { std::vector<int> v = {1,2,,,3,{4,5}}; }";
166   std::string Expected = "void f() { std::vector<int> v = {1,2,3,{4,5}}; }";
167   EXPECT_EQ(Expected, cleanupAroundOffsets({40}, Code));
168 
169   Code = "int main() { f(1,,2,3,,4);}";
170   Expected = "int main() { f(1,2,3,4);}";
171   EXPECT_EQ(Expected, cleanupAroundOffsets({17, 22}, Code));
172 }
173 
174 TEST_F(CleanupTest, NoCleanupsForJavaScript) {
175   std::string Code = "function f() { var x = [a, b, , c]; }";
176   std::string Expected = "function f() { var x = [a, b, , c]; }";
177   const FormatStyle &Style = getGoogleStyle(FormatStyle::LK_JavaScript);
178 
179   EXPECT_EQ(Expected, cleanupAroundOffsets({30}, Code, Style));
180 }
181 
182 TEST_F(CleanupTest, TrailingCommaInParens) {
183   std::string Code = "int main() { f(,1,,2,3,f(1,2,),4,,);}";
184   std::string Expected = "int main() { f(1,2,3,f(1,2),4);}";
185   EXPECT_EQ(Expected, cleanupAroundOffsets({15, 18, 29, 33}, Code));
186 
187   // Lambda contents are also checked for trailing commas.
188   Code = "int main() { [](){f(,1,,2,3,f(1,2,),4,,);}();}";
189   Expected = "int main() { [](){f(1,2,3,f(1,2),4);}();}";
190   EXPECT_EQ(Expected, cleanupAroundOffsets({20, 23, 34, 38}, Code));
191 }
192 
193 TEST_F(CleanupTest, TrailingCommaInBraces) {
194   // Trailing comma is allowed in brace list.
195   // If there was trailing comma in the original code, then trailing comma is
196   // preserved. In this example, element between the last two commas is deleted
197   // causing the second-last comma to be redundant.
198   std::string Code = "void f() { std::vector<int> v = {1,2,3,,}; }";
199   std::string Expected = "void f() { std::vector<int> v = {1,2,3,}; }";
200   EXPECT_EQ(Expected, cleanupAroundOffsets({39}, Code));
201 
202   // If there was no trailing comma in the original code, then trailing comma
203   // introduced by replacements should be cleaned up. In this example, the
204   // element after the last comma is deleted causing the last comma to be
205   // redundant.
206   Code = "void f() { std::vector<int> v = {1,2,3,}; }";
207   // FIXME: redundant trailing comma should be removed.
208   Expected = "void f() { std::vector<int> v = {1,2,3,}; }";
209   EXPECT_EQ(Expected, cleanupAroundOffsets({39}, Code));
210 
211   // Still no trailing comma in the original code, but two elements are deleted,
212   // which makes it seems like there was trailing comma.
213   Code = "void f() { std::vector<int> v = {1, 2, 3, , }; }";
214   // FIXME: redundant trailing comma should also be removed.
215   Expected = "void f() { std::vector<int> v = {1, 2, 3,  }; }";
216   EXPECT_EQ(Expected, cleanupAroundOffsets({42, 44}, Code));
217 }
218 
219 TEST_F(CleanupTest, CtorInitializationBracesInParens) {
220   std::string Code = "class A {\nA() : x({1}),, {} };";
221   std::string Expected = "class A {\nA() : x({1}) {} };";
222   EXPECT_EQ(Expected, cleanupAroundOffsets({24, 26}, Code));
223 }
224 
225 TEST_F(CleanupTest, RedundantCommaNotInAffectedRanges) {
226   std::string Code =
227       "class A {\nA() : x({1}), /* comment */, { int x = 0; } };";
228   std::string Expected =
229       "class A {\nA() : x({1}), /* comment */, { int x = 0; } };";
230   // Set the affected range to be "int x = 0", which does not intercept the
231   // constructor initialization list.
232   std::vector<tooling::Range> Ranges(1, tooling::Range(42, 9));
233   std::string Result = cleanup(Code, Ranges);
234   EXPECT_EQ(Expected, Result);
235 
236   Code = "class A {\nA() : x(1), {} };";
237   Expected = "class A {\nA() : x(1), {} };";
238   // No range. Fixer should do nothing.
239   Ranges.clear();
240   Result = cleanup(Code, Ranges);
241   EXPECT_EQ(Expected, Result);
242 }
243 
244 TEST_F(CleanupTest, RemoveCommentsAroundDeleteCode) {
245   std::string Code =
246       "class A {\nA() : x({1}), /* comment */, /* comment */ {} };";
247   std::string Expected = "class A {\nA() : x({1}) {} };";
248   EXPECT_EQ(Expected, cleanupAroundOffsets({25, 40}, Code));
249 
250   Code = "class A {\nA() : x({1}), // comment\n {} };";
251   Expected = "class A {\nA() : x({1})\n {} };";
252   EXPECT_EQ(Expected, cleanupAroundOffsets({25}, Code));
253 
254   Code = "class A {\nA() : x({1}), // comment\n , y(1),{} };";
255   Expected = "class A {\nA() : x({1}),  y(1){} };";
256   EXPECT_EQ(Expected, cleanupAroundOffsets({38}, Code));
257 
258   Code = "class A {\nA() : x({1}), \n/* comment */, y(1),{} };";
259   Expected = "class A {\nA() : x({1}), \n y(1){} };";
260   EXPECT_EQ(Expected, cleanupAroundOffsets({40}, Code));
261 
262   Code = "class A {\nA() : , // comment\n y(1),{} };";
263   Expected = "class A {\nA() :  // comment\n y(1){} };";
264   EXPECT_EQ(Expected, cleanupAroundOffsets({17}, Code));
265 
266   Code = "class A {\nA() // comment\n : ,,{} };";
267   Expected = "class A {\nA() // comment\n {} };";
268   EXPECT_EQ(Expected, cleanupAroundOffsets({30}, Code));
269 
270   Code = "class A {\nA() // comment\n : ,,=default; };";
271   Expected = "class A {\nA() // comment\n =default; };";
272   EXPECT_EQ(Expected, cleanupAroundOffsets({30}, Code));
273 }
274 
275 TEST_F(CleanupTest, CtorInitializerInNamespace) {
276   std::string Code = "namespace A {\n"
277                      "namespace B {\n" // missing r_brace
278                      "} // namespace A\n\n"
279                      "namespace C {\n"
280                      "class A { A() : x(0),, {} };\n"
281                      "inline namespace E { namespace { } }\n"
282                      "}";
283   std::string Expected = "namespace A {\n"
284                          "\n\n\nnamespace C {\n"
285                          "class A { A() : x(0) {} };\n   \n"
286                          "}";
287   std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
288   std::string Result = cleanup(Code, Ranges);
289   EXPECT_EQ(Expected, Result);
290 }
291 
292 class CleanUpReplacementsTest : public ReplacementTest {
293 protected:
294   tooling::Replacement createReplacement(unsigned Offset, unsigned Length,
295                                          StringRef Text) {
296     return tooling::Replacement(FileName, Offset, Length, Text);
297   }
298 
299   tooling::Replacement createInsertion(StringRef IncludeDirective) {
300     return createReplacement(UINT_MAX, 0, IncludeDirective);
301   }
302 
303   tooling::Replacement createDeletion(StringRef HeaderName) {
304     return createReplacement(UINT_MAX, 1, HeaderName);
305   }
306 
307   inline std::string apply(StringRef Code,
308                            const tooling::Replacements &Replaces) {
309     auto CleanReplaces = cleanupAroundReplacements(Code, Replaces, Style);
310     EXPECT_TRUE(static_cast<bool>(CleanReplaces))
311         << llvm::toString(CleanReplaces.takeError()) << "\n";
312     auto Result = applyAllReplacements(Code, *CleanReplaces);
313     EXPECT_TRUE(static_cast<bool>(Result));
314     return *Result;
315   }
316 
317   inline std::string formatAndApply(StringRef Code,
318                                     const tooling::Replacements &Replaces) {
319     auto CleanReplaces = cleanupAroundReplacements(Code, Replaces, Style);
320     EXPECT_TRUE(static_cast<bool>(CleanReplaces))
321         << llvm::toString(CleanReplaces.takeError()) << "\n";
322     auto FormattedReplaces = formatReplacements(Code, *CleanReplaces, Style);
323     EXPECT_TRUE(static_cast<bool>(FormattedReplaces))
324         << llvm::toString(FormattedReplaces.takeError()) << "\n";
325     auto Result = applyAllReplacements(Code, *FormattedReplaces);
326     EXPECT_TRUE(static_cast<bool>(Result));
327     return *Result;
328   }
329 
330   int getOffset(StringRef Code, int Line, int Column) {
331     RewriterTestContext Context;
332     FileID ID = Context.createInMemoryFile(FileName, Code);
333     auto DecomposedLocation =
334         Context.Sources.getDecomposedLoc(Context.getLocation(ID, Line, Column));
335     return DecomposedLocation.second;
336   }
337 
338   const std::string FileName = "fix.cpp";
339   FormatStyle Style = getLLVMStyle();
340 };
341 
342 TEST_F(CleanUpReplacementsTest, FixOnlyAffectedCodeAfterReplacements) {
343   std::string Code = "namespace A {\n"
344                      "namespace B {\n"
345                      "  int x;\n"
346                      "} // namespace B\n"
347                      "} // namespace A\n"
348                      "\n"
349                      "namespace C {\n"
350                      "namespace D { int i; }\n"
351                      "inline namespace E { namespace { int y; } }\n"
352                      "int x=     0;"
353                      "}";
354   std::string Expected = "\n\nnamespace C {\n"
355                          "namespace D { int i; }\n\n"
356                          "int x=     0;"
357                          "}";
358   tooling::Replacements Replaces =
359       toReplacements({createReplacement(getOffset(Code, 3, 3), 6, ""),
360                       createReplacement(getOffset(Code, 9, 34), 6, "")});
361 
362   EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
363 }
364 
365 TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesLLVMStyle) {
366   std::string Code = "#include \"x/fix.h\"\n"
367                      "#include \"a.h\"\n"
368                      "#include \"b.h\"\n"
369                      "#include \"z.h\"\n"
370                      "#include \"clang/Format/Format.h\"\n"
371                      "#include <memory>\n";
372   std::string Expected = "#include \"x/fix.h\"\n"
373                          "#include \"a.h\"\n"
374                          "#include \"b.h\"\n"
375                          "#include \"new/new.h\"\n"
376                          "#include \"z.h\"\n"
377                          "#include \"clang/Format/Format.h\"\n"
378                          "#include <list>\n"
379                          "#include <memory>\n";
380   tooling::Replacements Replaces =
381       toReplacements({createInsertion("#include <list>"),
382                       createInsertion("#include \"new/new.h\"")});
383   EXPECT_EQ(Expected, apply(Code, Replaces));
384 }
385 
386 TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesGoogleStyle) {
387   std::string Code = "#include \"x/fix.h\"\n"
388                      "\n"
389                      "#include <vector>\n"
390                      "\n"
391                      "#include \"y/a.h\"\n"
392                      "#include \"z/b.h\"\n";
393   std::string Expected = "#include \"x/fix.h\"\n"
394                          "\n"
395                          "#include <list>\n"
396                          "#include <vector>\n"
397                          "\n"
398                          "#include \"x/x.h\"\n"
399                          "#include \"y/a.h\"\n"
400                          "#include \"z/b.h\"\n";
401   tooling::Replacements Replaces =
402       toReplacements({createInsertion("#include <list>"),
403                       createInsertion("#include \"x/x.h\"")});
404   Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
405   EXPECT_EQ(Expected, apply(Code, Replaces));
406 }
407 
408 TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortLLVM) {
409   std::string Code = "\nint x;";
410   std::string Expected = "\n#include \"fix.h\"\n"
411                          "#include \"a.h\"\n"
412                          "#include \"b.h\"\n"
413                          "#include \"c.h\"\n"
414                          "#include <list>\n"
415                          "#include <vector>\n"
416                          "int x;";
417   tooling::Replacements Replaces = toReplacements(
418       {createInsertion("#include \"a.h\""), createInsertion("#include \"c.h\""),
419        createInsertion("#include \"b.h\""),
420        createInsertion("#include <vector>"), createInsertion("#include <list>"),
421        createInsertion("#include \"fix.h\"")});
422   EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
423 }
424 
425 TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortGoogle) {
426   std::string Code = "\nint x;";
427   std::string Expected = "\n#include \"fix.h\"\n"
428                          "\n"
429                          "#include <list>\n"
430                          "#include <vector>\n"
431                          "\n"
432                          "#include \"a.h\"\n"
433                          "#include \"b.h\"\n"
434                          "#include \"c.h\"\n"
435                          "int x;";
436   tooling::Replacements Replaces = toReplacements(
437       {createInsertion("#include \"a.h\""), createInsertion("#include \"c.h\""),
438        createInsertion("#include \"b.h\""),
439        createInsertion("#include <vector>"), createInsertion("#include <list>"),
440        createInsertion("#include \"fix.h\"")});
441   Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
442   EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
443 }
444 
445 TEST_F(CleanUpReplacementsTest, NoNewLineAtTheEndOfCodeMultipleInsertions) {
446   std::string Code = "#include <map>";
447   // FIXME: a better behavior is to only append on newline to Code, but this
448   // case should be rare in practice.
449   std::string Expected =
450       "#include <map>\n#include <string>\n\n#include <vector>\n";
451   tooling::Replacements Replaces =
452       toReplacements({createInsertion("#include <string>"),
453                       createInsertion("#include <vector>")});
454   EXPECT_EQ(Expected, apply(Code, Replaces));
455 }
456 
457 
458 TEST_F(CleanUpReplacementsTest, FormatCorrectLineWhenHeadersAreInserted) {
459   std::string Code = "\n"
460                      "int x;\n"
461                      "int    a;\n"
462                      "int    a;\n"
463                      "int    a;";
464 
465   std::string Expected = "\n#include \"x.h\"\n"
466                          "#include \"y.h\"\n"
467                          "#include \"clang/x/x.h\"\n"
468                          "#include <list>\n"
469                          "#include <vector>\n"
470                          "int x;\n"
471                          "int    a;\n"
472                          "int b;\n"
473                          "int    a;";
474   tooling::Replacements Replaces = toReplacements(
475       {createReplacement(getOffset(Code, 4, 8), 1, "b"),
476        createInsertion("#include <vector>"), createInsertion("#include <list>"),
477        createInsertion("#include \"clang/x/x.h\""),
478        createInsertion("#include \"y.h\""),
479        createInsertion("#include \"x.h\"")});
480   EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
481 }
482 
483 TEST_F(CleanUpReplacementsTest, SimpleDeleteIncludes) {
484   std::string Code = "#include \"abc.h\"\n"
485                      "#include \"xyz.h\" // comment\n"
486                      "#include \"xyz\"\n"
487                      "int x;\n";
488   std::string Expected = "#include \"xyz\"\n"
489                          "int x;\n";
490   tooling::Replacements Replaces =
491       toReplacements({createDeletion("abc.h"), createDeletion("xyz.h")});
492   EXPECT_EQ(Expected, apply(Code, Replaces));
493 }
494 
495 TEST_F(CleanUpReplacementsTest, InsertionAndDeleteHeader) {
496   std::string Code = "#include \"a.h\"\n"
497                      "\n"
498                      "#include <vector>\n";
499   std::string Expected = "#include \"a.h\"\n"
500                          "\n"
501                          "#include <map>\n";
502   tooling::Replacements Replaces = toReplacements(
503       {createDeletion("<vector>"), createInsertion("#include <map>")});
504   EXPECT_EQ(Expected, apply(Code, Replaces));
505 }
506 
507 } // end namespace
508 } // end namespace format
509 } // end namespace clang
510