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