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/RewriterTestContext.h" 13 #include "clang/Tooling/Core/Replacement.h" 14 15 #include "gtest/gtest.h" 16 17 namespace clang { 18 namespace format { 19 namespace { 20 21 class CleanupTest : public ::testing::Test { 22 protected: 23 std::string cleanup(llvm::StringRef Code, 24 const std::vector<tooling::Range> &Ranges, 25 const FormatStyle &Style = getLLVMStyle()) { 26 tooling::Replacements Replaces = format::cleanup(Style, Code, Ranges); 27 28 std::string Result = applyAllReplacements(Code, Replaces); 29 EXPECT_NE("", Result); 30 return Result; 31 } 32 }; 33 34 TEST_F(CleanupTest, DeleteEmptyNamespaces) { 35 std::string Code = "namespace A {\n" 36 "namespace B {\n" 37 "} // namespace B\n" 38 "} // namespace A\n\n" 39 "namespace C {\n" 40 "namespace D { int i; }\n" 41 "inline namespace E { namespace { } }\n" 42 "}"; 43 std::string Expected = "\n\n\n\n\nnamespace C {\n" 44 "namespace D { int i; }\n \n" 45 "}"; 46 std::vector<tooling::Range> Ranges; 47 Ranges.push_back(tooling::Range(28, 0)); 48 Ranges.push_back(tooling::Range(91, 6)); 49 Ranges.push_back(tooling::Range(132, 0)); 50 std::string Result = cleanup(Code, Ranges); 51 EXPECT_EQ(Expected, Result); 52 } 53 54 TEST_F(CleanupTest, NamespaceWithSyntaxError) { 55 std::string Code = "namespace A {\n" 56 "namespace B {\n" // missing r_brace 57 "} // namespace A\n\n" 58 "namespace C {\n" 59 "namespace D int i; }\n" 60 "inline namespace E { namespace { } }\n" 61 "}"; 62 std::string Expected = "namespace A {\n" 63 "\n\n\nnamespace C {\n" 64 "namespace D int i; }\n \n" 65 "}"; 66 std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size())); 67 std::string Result = cleanup(Code, Ranges); 68 EXPECT_EQ(Expected, Result); 69 } 70 71 TEST_F(CleanupTest, EmptyNamespaceNotAffected) { 72 std::string Code = "namespace A {\n\n" 73 "namespace {\n\n}}"; 74 // Even though the namespaces are empty, but the inner most empty namespace 75 // block is not affected by the changed ranges. 76 std::string Expected = "namespace A {\n\n" 77 "namespace {\n\n}}"; 78 // Set the changed range to be the second "\n". 79 std::vector<tooling::Range> Ranges(1, tooling::Range(14, 0)); 80 std::string Result = cleanup(Code, Ranges); 81 EXPECT_EQ(Expected, Result); 82 } 83 84 TEST_F(CleanupTest, EmptyNamespaceWithCommentsNoBreakBeforeBrace) { 85 std::string Code = "namespace A {\n" 86 "namespace B {\n" 87 "// Yo\n" 88 "} // namespace B\n" 89 "} // namespace A\n" 90 "namespace C { // Yo\n" 91 "}"; 92 std::string Expected = "\n\n\n\n\n\n"; 93 std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size())); 94 std::string Result = cleanup(Code, Ranges); 95 EXPECT_EQ(Expected, Result); 96 } 97 98 TEST_F(CleanupTest, EmptyNamespaceWithCommentsBreakBeforeBrace) { 99 std::string Code = "namespace A\n" 100 "/* Yo */ {\n" 101 "namespace B\n" 102 "{\n" 103 "// Yo\n" 104 "} // namespace B\n" 105 "} // namespace A\n" 106 "namespace C\n" 107 "{ // Yo\n" 108 "}\n"; 109 std::string Expected = "\n\n\n\n\n\n\n\n\n\n"; 110 std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size())); 111 FormatStyle Style = getLLVMStyle(); 112 Style.BraceWrapping.AfterNamespace = true; 113 std::string Result = cleanup(Code, Ranges, Style); 114 EXPECT_EQ(Expected, Result); 115 } 116 117 TEST_F(CleanupTest, CtorInitializationSimpleRedundantComma) { 118 std::string Code = "class A {\nA() : , {} };"; 119 std::string Expected = "class A {\nA() {} };"; 120 std::vector<tooling::Range> Ranges; 121 Ranges.push_back(tooling::Range(17, 0)); 122 Ranges.push_back(tooling::Range(19, 0)); 123 std::string Result = cleanup(Code, Ranges); 124 EXPECT_EQ(Expected, Result); 125 126 Code = "class A {\nA() : x(1), {} };"; 127 Expected = "class A {\nA() : x(1) {} };"; 128 Ranges.clear(); 129 Ranges.push_back(tooling::Range(23, 0)); 130 Result = cleanup(Code, Ranges); 131 EXPECT_EQ(Expected, Result); 132 133 Code = "class A {\nA() :,,,,{} };"; 134 Expected = "class A {\nA() {} };"; 135 Ranges.clear(); 136 Ranges.push_back(tooling::Range(15, 0)); 137 Result = cleanup(Code, Ranges); 138 EXPECT_EQ(Expected, Result); 139 } 140 141 TEST_F(CleanupTest, ListSimpleRedundantComma) { 142 std::string Code = "void f() { std::vector<int> v = {1,2,,,3,{4,5}}; }"; 143 std::string Expected = "void f() { std::vector<int> v = {1,2,3,{4,5}}; }"; 144 std::vector<tooling::Range> Ranges; 145 Ranges.push_back(tooling::Range(40, 0)); 146 std::string Result = cleanup(Code, Ranges); 147 EXPECT_EQ(Expected, Result); 148 149 Code = "int main() { f(1,,2,3,,4);}"; 150 Expected = "int main() { f(1,2,3,4);}"; 151 Ranges.clear(); 152 Ranges.push_back(tooling::Range(17, 0)); 153 Ranges.push_back(tooling::Range(22, 0)); 154 Result = cleanup(Code, Ranges); 155 EXPECT_EQ(Expected, Result); 156 } 157 158 TEST_F(CleanupTest, CtorInitializationBracesInParens) { 159 std::string Code = "class A {\nA() : x({1}),, {} };"; 160 std::string Expected = "class A {\nA() : x({1}) {} };"; 161 std::vector<tooling::Range> Ranges; 162 Ranges.push_back(tooling::Range(24, 0)); 163 Ranges.push_back(tooling::Range(26, 0)); 164 std::string Result = cleanup(Code, Ranges); 165 EXPECT_EQ(Expected, Result); 166 } 167 168 TEST_F(CleanupTest, RedundantCommaNotInAffectedRanges) { 169 std::string Code = 170 "class A {\nA() : x({1}), /* comment */, { int x = 0; } };"; 171 std::string Expected = 172 "class A {\nA() : x({1}), /* comment */, { int x = 0; } };"; 173 // Set the affected range to be "int x = 0", which does not intercept the 174 // constructor initialization list. 175 std::vector<tooling::Range> Ranges(1, tooling::Range(42, 9)); 176 std::string Result = cleanup(Code, Ranges); 177 EXPECT_EQ(Expected, Result); 178 179 Code = "class A {\nA() : x(1), {} };"; 180 Expected = "class A {\nA() : x(1), {} };"; 181 // No range. Fixer should do nothing. 182 Ranges.clear(); 183 Result = cleanup(Code, Ranges); 184 EXPECT_EQ(Expected, Result); 185 } 186 187 // FIXME: delete comments too. 188 TEST_F(CleanupTest, CtorInitializationCommentAroundCommas) { 189 // Remove redundant commas around comment. 190 std::string Code = "class A {\nA() : x({1}), /* comment */, {} };"; 191 std::string Expected = "class A {\nA() : x({1}) /* comment */ {} };"; 192 std::vector<tooling::Range> Ranges; 193 Ranges.push_back(tooling::Range(25, 0)); 194 Ranges.push_back(tooling::Range(40, 0)); 195 std::string Result = cleanup(Code, Ranges); 196 EXPECT_EQ(Expected, Result); 197 198 // Remove trailing comma and ignore comment. 199 Code = "class A {\nA() : x({1}), // comment\n{} };"; 200 Expected = "class A {\nA() : x({1}) // comment\n{} };"; 201 Ranges = std::vector<tooling::Range>(1, tooling::Range(25, 0)); 202 Result = cleanup(Code, Ranges); 203 EXPECT_EQ(Expected, Result); 204 205 // Remove trailing comma and ignore comment. 206 Code = "class A {\nA() : x({1}), // comment\n , y(1),{} };"; 207 Expected = "class A {\nA() : x({1}), // comment\n y(1){} };"; 208 Ranges = std::vector<tooling::Range>(1, tooling::Range(38, 0)); 209 Result = cleanup(Code, Ranges); 210 EXPECT_EQ(Expected, Result); 211 212 // Remove trailing comma and ignore comment. 213 Code = "class A {\nA() : x({1}), \n/* comment */, y(1),{} };"; 214 Expected = "class A {\nA() : x({1}), \n/* comment */ y(1){} };"; 215 Ranges = std::vector<tooling::Range>(1, tooling::Range(40, 0)); 216 Result = cleanup(Code, Ranges); 217 EXPECT_EQ(Expected, Result); 218 219 // Remove trailing comma and ignore comment. 220 Code = "class A {\nA() : , // comment\n y(1),{} };"; 221 Expected = "class A {\nA() : // comment\n y(1){} };"; 222 Ranges = std::vector<tooling::Range>(1, tooling::Range(17, 0)); 223 Result = cleanup(Code, Ranges); 224 EXPECT_EQ(Expected, Result); 225 } 226 227 TEST_F(CleanupTest, CtorInitializerInNamespace) { 228 std::string Code = "namespace A {\n" 229 "namespace B {\n" // missing r_brace 230 "} // namespace A\n\n" 231 "namespace C {\n" 232 "class A { A() : x(0),, {} };\n" 233 "inline namespace E { namespace { } }\n" 234 "}"; 235 std::string Expected = "namespace A {\n" 236 "\n\n\nnamespace C {\n" 237 "class A { A() : x(0) {} };\n \n" 238 "}"; 239 std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size())); 240 std::string Result = cleanup(Code, Ranges); 241 EXPECT_EQ(Expected, Result); 242 } 243 244 class CleanUpReplacementsTest : public ::testing::Test { 245 protected: 246 tooling::Replacement createReplacement(SourceLocation Start, unsigned Length, 247 llvm::StringRef ReplacementText) { 248 return tooling::Replacement(Context.Sources, Start, Length, 249 ReplacementText); 250 } 251 252 RewriterTestContext Context; 253 }; 254 255 TEST_F(CleanUpReplacementsTest, FixOnlyAffectedCodeAfterReplacements) { 256 std::string Code = "namespace A {\n" 257 "namespace B {\n" 258 " int x;\n" 259 "} // namespace B\n" 260 "} // namespace A\n" 261 "\n" 262 "namespace C {\n" 263 "namespace D { int i; }\n" 264 "inline namespace E { namespace { int y; } }\n" 265 "int x= 0;" 266 "}"; 267 std::string Expected = "\n\nnamespace C {\n" 268 "namespace D { int i; }\n\n" 269 "int x= 0;" 270 "}"; 271 FileID ID = Context.createInMemoryFile("fix.cpp", Code); 272 tooling::Replacements Replaces; 273 Replaces.insert(tooling::Replacement(Context.Sources, 274 Context.getLocation(ID, 3, 3), 6, "")); 275 Replaces.insert(tooling::Replacement(Context.Sources, 276 Context.getLocation(ID, 9, 34), 6, "")); 277 278 format::FormatStyle Style = format::getLLVMStyle(); 279 auto FinalReplaces = formatReplacements( 280 Code, cleanupAroundReplacements(Code, Replaces, Style), Style); 281 EXPECT_EQ(Expected, applyAllReplacements(Code, FinalReplaces)); 282 } 283 284 } // end namespace 285 } // end namespace format 286 } // end namespace clang 287