brintos

brintos / llvm-project-archived public Read only

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