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 
188 TEST_F(CleanupTest, TrailingCommaInBraces) {
189   // Trainling comma is allowed in brace list.
190   // If there was trailing comma in the original code, then trailing comma is
191   // preserved. In this example, element between the last two commas is deleted
192   // causing the second-last comma to be redundant.
193   std::string Code = "void f() { std::vector<int> v = {1,2,3,,}; }";
194   std::string Expected = "void f() { std::vector<int> v = {1,2,3,}; }";
195   EXPECT_EQ(Expected, cleanupAroundOffsets({39}, Code));
196 
197   // If there was no trailing comma in the original code, then trainling comma
198   // introduced by replacements should be cleaned up. In this example, the
199   // element after the last comma is deleted causing the last comma to be
200   // redundant.
201   Code = "void f() { std::vector<int> v = {1,2,3,}; }";
202   // FIXME: redundant trailing comma should be removed.
203   Expected = "void f() { std::vector<int> v = {1,2,3,}; }";
204   EXPECT_EQ(Expected, cleanupAroundOffsets({39}, Code));
205 
206   // Still no trailing comma in the original code, but two elements are deleted,
207   // which makes it seems like there was trailing comma.
208   Code = "void f() { std::vector<int> v = {1, 2, 3, , }; }";
209   // FIXME: redundant trailing comma should also be removed.
210   Expected = "void f() { std::vector<int> v = {1, 2, 3,  }; }";
211   EXPECT_EQ(Expected, cleanupAroundOffsets({42, 44}, Code));
212 }
213 
214 TEST_F(CleanupTest, CtorInitializationBracesInParens) {
215   std::string Code = "class A {\nA() : x({1}),, {} };";
216   std::string Expected = "class A {\nA() : x({1}) {} };";
217   EXPECT_EQ(Expected, cleanupAroundOffsets({24, 26}, Code));
218 }
219 
220 TEST_F(CleanupTest, RedundantCommaNotInAffectedRanges) {
221   std::string Code =
222       "class A {\nA() : x({1}), /* comment */, { int x = 0; } };";
223   std::string Expected =
224       "class A {\nA() : x({1}), /* comment */, { int x = 0; } };";
225   // Set the affected range to be "int x = 0", which does not intercept the
226   // constructor initialization list.
227   std::vector<tooling::Range> Ranges(1, tooling::Range(42, 9));
228   std::string Result = cleanup(Code, Ranges);
229   EXPECT_EQ(Expected, Result);
230 
231   Code = "class A {\nA() : x(1), {} };";
232   Expected = "class A {\nA() : x(1), {} };";
233   // No range. Fixer should do nothing.
234   Ranges.clear();
235   Result = cleanup(Code, Ranges);
236   EXPECT_EQ(Expected, Result);
237 }
238 
239 TEST_F(CleanupTest, RemoveCommentsAroundDeleteCode) {
240   std::string Code =
241       "class A {\nA() : x({1}), /* comment */, /* comment */ {} };";
242   std::string Expected = "class A {\nA() : x({1}) {} };";
243   EXPECT_EQ(Expected, cleanupAroundOffsets({25, 40}, Code));
244 
245   Code = "class A {\nA() : x({1}), // comment\n {} };";
246   Expected = "class A {\nA() : x({1})\n {} };";
247   EXPECT_EQ(Expected, cleanupAroundOffsets({25}, Code));
248 
249   Code = "class A {\nA() : x({1}), // comment\n , y(1),{} };";
250   Expected = "class A {\nA() : x({1}),  y(1){} };";
251   EXPECT_EQ(Expected, cleanupAroundOffsets({38}, Code));
252 
253   Code = "class A {\nA() : x({1}), \n/* comment */, y(1),{} };";
254   Expected = "class A {\nA() : x({1}), \n y(1){} };";
255   EXPECT_EQ(Expected, cleanupAroundOffsets({40}, Code));
256 
257   Code = "class A {\nA() : , // comment\n y(1),{} };";
258   Expected = "class A {\nA() :  // comment\n y(1){} };";
259   EXPECT_EQ(Expected, cleanupAroundOffsets({17}, Code));
260 
261   Code = "class A {\nA() // comment\n : ,,{} };";
262   Expected = "class A {\nA() // comment\n {} };";
263   EXPECT_EQ(Expected, cleanupAroundOffsets({30}, Code));
264 
265   Code = "class A {\nA() // comment\n : ,,=default; };";
266   Expected = "class A {\nA() // comment\n =default; };";
267   EXPECT_EQ(Expected, cleanupAroundOffsets({30}, Code));
268 }
269 
270 TEST_F(CleanupTest, CtorInitializerInNamespace) {
271   std::string Code = "namespace A {\n"
272                      "namespace B {\n" // missing r_brace
273                      "} // namespace A\n\n"
274                      "namespace C {\n"
275                      "class A { A() : x(0),, {} };\n"
276                      "inline namespace E { namespace { } }\n"
277                      "}";
278   std::string Expected = "namespace A {\n"
279                          "\n\n\nnamespace C {\n"
280                          "class A { A() : x(0) {} };\n   \n"
281                          "}";
282   std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
283   std::string Result = cleanup(Code, Ranges);
284   EXPECT_EQ(Expected, Result);
285 }
286 
287 class CleanUpReplacementsTest : public ReplacementTest {
288 protected:
289   tooling::Replacement createReplacement(unsigned Offset, unsigned Length,
290                                          StringRef Text) {
291     return tooling::Replacement(FileName, Offset, Length, Text);
292   }
293 
294   tooling::Replacement createInsertion(StringRef IncludeDirective) {
295     return createReplacement(UINT_MAX, 0, IncludeDirective);
296   }
297 
298   tooling::Replacement createDeletion(StringRef HeaderName) {
299     return createReplacement(UINT_MAX, 1, HeaderName);
300   }
301 
302   inline std::string apply(StringRef Code,
303                            const tooling::Replacements &Replaces) {
304     auto CleanReplaces = cleanupAroundReplacements(Code, Replaces, Style);
305     EXPECT_TRUE(static_cast<bool>(CleanReplaces))
306         << llvm::toString(CleanReplaces.takeError()) << "\n";
307     auto Result = applyAllReplacements(Code, *CleanReplaces);
308     EXPECT_TRUE(static_cast<bool>(Result));
309     return *Result;
310   }
311 
312   inline std::string formatAndApply(StringRef Code,
313                                     const tooling::Replacements &Replaces) {
314     auto CleanReplaces = cleanupAroundReplacements(Code, Replaces, Style);
315     EXPECT_TRUE(static_cast<bool>(CleanReplaces))
316         << llvm::toString(CleanReplaces.takeError()) << "\n";
317     auto FormattedReplaces = formatReplacements(Code, *CleanReplaces, Style);
318     EXPECT_TRUE(static_cast<bool>(FormattedReplaces))
319         << llvm::toString(FormattedReplaces.takeError()) << "\n";
320     auto Result = applyAllReplacements(Code, *FormattedReplaces);
321     EXPECT_TRUE(static_cast<bool>(Result));
322     return *Result;
323   }
324 
325   int getOffset(StringRef Code, int Line, int Column) {
326     RewriterTestContext Context;
327     FileID ID = Context.createInMemoryFile(FileName, Code);
328     auto DecomposedLocation =
329         Context.Sources.getDecomposedLoc(Context.getLocation(ID, Line, Column));
330     return DecomposedLocation.second;
331   }
332 
333   const std::string FileName = "fix.cpp";
334   FormatStyle Style = getLLVMStyle();
335 };
336 
337 TEST_F(CleanUpReplacementsTest, FixOnlyAffectedCodeAfterReplacements) {
338   std::string Code = "namespace A {\n"
339                      "namespace B {\n"
340                      "  int x;\n"
341                      "} // namespace B\n"
342                      "} // namespace A\n"
343                      "\n"
344                      "namespace C {\n"
345                      "namespace D { int i; }\n"
346                      "inline namespace E { namespace { int y; } }\n"
347                      "int x=     0;"
348                      "}";
349   std::string Expected = "\n\nnamespace C {\n"
350                          "namespace D { int i; }\n\n"
351                          "int x=     0;"
352                          "}";
353   tooling::Replacements Replaces =
354       toReplacements({createReplacement(getOffset(Code, 3, 3), 6, ""),
355                       createReplacement(getOffset(Code, 9, 34), 6, "")});
356 
357   EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
358 }
359 
360 TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesLLVMStyle) {
361   std::string Code = "#include \"x/fix.h\"\n"
362                      "#include \"a.h\"\n"
363                      "#include \"b.h\"\n"
364                      "#include \"z.h\"\n"
365                      "#include \"clang/Format/Format.h\"\n"
366                      "#include <memory>\n";
367   std::string Expected = "#include \"x/fix.h\"\n"
368                          "#include \"a.h\"\n"
369                          "#include \"b.h\"\n"
370                          "#include \"new/new.h\"\n"
371                          "#include \"z.h\"\n"
372                          "#include \"clang/Format/Format.h\"\n"
373                          "#include <list>\n"
374                          "#include <memory>\n";
375   tooling::Replacements Replaces =
376       toReplacements({createInsertion("#include <list>"),
377                       createInsertion("#include \"new/new.h\"")});
378   EXPECT_EQ(Expected, apply(Code, Replaces));
379 }
380 
381 TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesGoogleStyle) {
382   std::string Code = "#include \"x/fix.h\"\n"
383                      "\n"
384                      "#include <vector>\n"
385                      "\n"
386                      "#include \"y/a.h\"\n"
387                      "#include \"z/b.h\"\n";
388   std::string Expected = "#include \"x/fix.h\"\n"
389                          "\n"
390                          "#include <list>\n"
391                          "#include <vector>\n"
392                          "\n"
393                          "#include \"x/x.h\"\n"
394                          "#include \"y/a.h\"\n"
395                          "#include \"z/b.h\"\n";
396   tooling::Replacements Replaces =
397       toReplacements({createInsertion("#include <list>"),
398                       createInsertion("#include \"x/x.h\"")});
399   Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
400   EXPECT_EQ(Expected, apply(Code, Replaces));
401 }
402 
403 TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortLLVM) {
404   std::string Code = "\nint x;";
405   std::string Expected = "\n#include \"fix.h\"\n"
406                          "#include \"a.h\"\n"
407                          "#include \"b.h\"\n"
408                          "#include \"c.h\"\n"
409                          "#include <list>\n"
410                          "#include <vector>\n"
411                          "int x;";
412   tooling::Replacements Replaces = toReplacements(
413       {createInsertion("#include \"a.h\""), createInsertion("#include \"c.h\""),
414        createInsertion("#include \"b.h\""),
415        createInsertion("#include <vector>"), createInsertion("#include <list>"),
416        createInsertion("#include \"fix.h\"")});
417   EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
418 }
419 
420 TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortGoogle) {
421   std::string Code = "\nint x;";
422   std::string Expected = "\n#include \"fix.h\"\n"
423                          "\n"
424                          "#include <list>\n"
425                          "#include <vector>\n"
426                          "\n"
427                          "#include \"a.h\"\n"
428                          "#include \"b.h\"\n"
429                          "#include \"c.h\"\n"
430                          "int x;";
431   tooling::Replacements Replaces = toReplacements(
432       {createInsertion("#include \"a.h\""), createInsertion("#include \"c.h\""),
433        createInsertion("#include \"b.h\""),
434        createInsertion("#include <vector>"), createInsertion("#include <list>"),
435        createInsertion("#include \"fix.h\"")});
436   Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
437   EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
438 }
439 
440 TEST_F(CleanUpReplacementsTest, NoNewLineAtTheEndOfCodeMultipleInsertions) {
441   std::string Code = "#include <map>";
442   // FIXME: a better behavior is to only append on newline to Code, but this
443   // case should be rare in practice.
444   std::string Expected =
445       "#include <map>\n#include <string>\n\n#include <vector>\n";
446   tooling::Replacements Replaces =
447       toReplacements({createInsertion("#include <string>"),
448                       createInsertion("#include <vector>")});
449   EXPECT_EQ(Expected, apply(Code, Replaces));
450 }
451 
452 
453 TEST_F(CleanUpReplacementsTest, FormatCorrectLineWhenHeadersAreInserted) {
454   std::string Code = "\n"
455                      "int x;\n"
456                      "int    a;\n"
457                      "int    a;\n"
458                      "int    a;";
459 
460   std::string Expected = "\n#include \"x.h\"\n"
461                          "#include \"y.h\"\n"
462                          "#include \"clang/x/x.h\"\n"
463                          "#include <list>\n"
464                          "#include <vector>\n"
465                          "int x;\n"
466                          "int    a;\n"
467                          "int b;\n"
468                          "int    a;";
469   tooling::Replacements Replaces = toReplacements(
470       {createReplacement(getOffset(Code, 4, 8), 1, "b"),
471        createInsertion("#include <vector>"), createInsertion("#include <list>"),
472        createInsertion("#include \"clang/x/x.h\""),
473        createInsertion("#include \"y.h\""),
474        createInsertion("#include \"x.h\"")});
475   EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
476 }
477 
478 TEST_F(CleanUpReplacementsTest, SimpleDeleteIncludes) {
479   std::string Code = "#include \"abc.h\"\n"
480                      "#include \"xyz.h\" // comment\n"
481                      "#include \"xyz\"\n"
482                      "int x;\n";
483   std::string Expected = "#include \"xyz\"\n"
484                          "int x;\n";
485   tooling::Replacements Replaces =
486       toReplacements({createDeletion("abc.h"), createDeletion("xyz.h")});
487   EXPECT_EQ(Expected, apply(Code, Replaces));
488 }
489 
490 TEST_F(CleanUpReplacementsTest, InsertionAndDeleteHeader) {
491   std::string Code = "#include \"a.h\"\n"
492                      "\n"
493                      "#include <vector>\n";
494   std::string Expected = "#include \"a.h\"\n"
495                          "\n"
496                          "#include <map>\n";
497   tooling::Replacements Replaces = toReplacements(
498       {createDeletion("<vector>"), createInsertion("#include <map>")});
499   EXPECT_EQ(Expected, apply(Code, Replaces));
500 }
501 
502 } // end namespace
503 } // end namespace format
504 } // end namespace clang
505