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