Clang Project

clang_source_code/unittests/Format/CleanupTest.cpp
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
17using clang::tooling::ReplacementTest;
18using clang::tooling::toReplacements;
19
20namespace clang {
21namespace format {
22namespace {
23
24class CleanupTest : public ::testing::Test {
25protected:
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<unsignedOffsets,
38                                   llvm::StringRef Code,
39                                   const FormatStyle &Style = getLLVMStyle()) {
40    std::vector<tooling::RangeRanges;
41    for (auto Offset : Offsets)
42      Ranges.push_back(tooling::Range(Offset, 0));
43    return cleanup(Code, Ranges, Style);
44  }
45};
46
47TEST_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({2891132}, Code));
60}
61
62TEST_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::RangeRanges(1tooling::Range(0Code.size()));
75  EXPECT_EQ(Expected, cleanup(CodeRanges));
76}
77
78TEST_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
89TEST_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::RangeRanges(1tooling::Range(0Code.size()));
99  std::string Result = cleanup(CodeRanges);
100  EXPECT_EQ(ExpectedResult);
101}
102
103TEST_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::RangeRanges(1tooling::Range(0Code.size()));
116  FormatStyle Style = getLLVMStyle();
117  Style.BraceWrapping.AfterNamespace = true;
118  std::string Result = cleanup(Code, Ranges, Style);
119  EXPECT_EQ(ExpectedResult);
120}
121
122TEST_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::RangeRanges(1tooling::Range(0Code.size()));
135  FormatStyle Style = getLLVMStyle();
136  std::string Result = cleanup(Code, Ranges, Style);
137  EXPECT_EQ(ExpectedResult);
138}
139
140TEST_F(CleanupTest, CtorInitializationSimpleRedundantComma) {
141  std::string Code = "class A {\nA() : , {} };";
142  std::string Expected = "class A {\nA()  {} };";
143  EXPECT_EQ(Expected, cleanupAroundOffsets({1719}, 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
154TEST_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
164TEST_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({1722}, Code));
172}
173
174TEST_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
182TEST_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({15182933}, Code));
186}
187
188TEST_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({4244}, Code));
212}
213
214TEST_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({2426}, Code));
218}
219
220TEST_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::RangeRanges(1tooling::Range(429));
228  std::string Result = cleanup(CodeRanges);
229  EXPECT_EQ(ExpectedResult);
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(CodeRanges);
236  EXPECT_EQ(ExpectedResult);
237}
238
239TEST_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({2540}, 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
270TEST_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::RangeRanges(1tooling::Range(0Code.size()));
283  std::string Result = cleanup(CodeRanges);
284  EXPECT_EQ(ExpectedResult);
285}
286
287class CleanUpReplacementsTest : public ReplacementTest {
288protected:
289  tooling::Replacement createReplacement(unsigned Offsetunsigned 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 Codeint Lineint Column) {
326    RewriterTestContext Context;
327    FileID ID = Context.createInMemoryFile(FileName, Code);
328    auto DecomposedLocation =
329        Context.Sources.getDecomposedLoc(Context.getLocation(IDLineColumn));
330    return DecomposedLocation.second;
331  }
332
333  const std::string FileName = "fix.cpp";
334  FormatStyle Style = getLLVMStyle();
335};
336
337TEST_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(Code33), 6""),
355                      createReplacement(getOffset(Code934), 6"")});
356
357  EXPECT_EQ(Expected, formatAndApply(CodeReplaces));
358}
359
360TEST_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(CodeReplaces));
379}
380
381TEST_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(CodeReplaces));
401}
402
403TEST_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(CodeReplaces));
418}
419
420TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortGoogle) {
421  std::string Code = "\nint x;";
422  std::string Expected = "\n#include \"fix.h\"\n"
423                         "#include <list>\n"
424                         "#include <vector>\n"
425                         "#include \"a.h\"\n"
426                         "#include \"b.h\"\n"
427                         "#include \"c.h\"\n"
428                         "int x;";
429  tooling::Replacements Replaces = toReplacements(
430      {createInsertion("#include \"a.h\""), createInsertion("#include \"c.h\""),
431       createInsertion("#include \"b.h\""),
432       createInsertion("#include <vector>"), createInsertion("#include <list>"),
433       createInsertion("#include \"fix.h\"")});
434  Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
435  EXPECT_EQ(Expected, formatAndApply(CodeReplaces));
436}
437
438TEST_F(CleanUpReplacementsTest, NoNewLineAtTheEndOfCodeMultipleInsertions) {
439  std::string Code = "#include <map>";
440  // FIXME: a better behavior is to only append on newline to Code, but this
441  // case should be rare in practice.
442  std::string Expected =
443      "#include <map>\n#include <string>\n\n#include <vector>\n";
444  tooling::Replacements Replaces =
445      toReplacements({createInsertion("#include <string>"),
446                      createInsertion("#include <vector>")});
447  EXPECT_EQ(Expected, apply(CodeReplaces));
448}
449
450
451TEST_F(CleanUpReplacementsTest, FormatCorrectLineWhenHeadersAreInserted) {
452  std::string Code = "\n"
453                     "int x;\n"
454                     "int    a;\n"
455                     "int    a;\n"
456                     "int    a;";
457
458  std::string Expected = "\n#include \"x.h\"\n"
459                         "#include \"y.h\"\n"
460                         "#include \"clang/x/x.h\"\n"
461                         "#include <list>\n"
462                         "#include <vector>\n"
463                         "int x;\n"
464                         "int    a;\n"
465                         "int b;\n"
466                         "int    a;";
467  tooling::Replacements Replaces = toReplacements(
468      {createReplacement(getOffset(Code48), 1"b"),
469       createInsertion("#include <vector>"), createInsertion("#include <list>"),
470       createInsertion("#include \"clang/x/x.h\""),
471       createInsertion("#include \"y.h\""),
472       createInsertion("#include \"x.h\"")});
473  EXPECT_EQ(Expected, formatAndApply(CodeReplaces));
474}
475
476TEST_F(CleanUpReplacementsTest, SimpleDeleteIncludes) {
477  std::string Code = "#include \"abc.h\"\n"
478                     "#include \"xyz.h\" // comment\n"
479                     "#include \"xyz\"\n"
480                     "int x;\n";
481  std::string Expected = "#include \"xyz\"\n"
482                         "int x;\n";
483  tooling::Replacements Replaces =
484      toReplacements({createDeletion("abc.h"), createDeletion("xyz.h")});
485  EXPECT_EQ(Expected, apply(CodeReplaces));
486}
487
488TEST_F(CleanUpReplacementsTest, InsertionAndDeleteHeader) {
489  std::string Code = "#include \"a.h\"\n"
490                     "\n"
491                     "#include <vector>\n";
492  std::string Expected = "#include \"a.h\"\n"
493                         "\n"
494                         "#include <map>\n";
495  tooling::Replacements Replaces = toReplacements(
496      {createDeletion("<vector>"), createInsertion("#include <map>")});
497  EXPECT_EQ(Expected, apply(CodeReplaces));
498}
499
500// end namespace
501// end namespace format
502// end namespace clang
503