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