1 //===-- IncludeFixerTest.cpp - Include fixer 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 "InMemorySymbolIndex.h" 10 #include "IncludeFixer.h" 11 #include "SymbolIndexManager.h" 12 #include "unittests/Tooling/RewriterTestContext.h" 13 #include "clang/Tooling/Tooling.h" 14 #include "gtest/gtest.h" 15 16 namespace clang { 17 namespace include_fixer { 18 namespace { 19 20 using find_all_symbols::SymbolInfo; 21 using find_all_symbols::SymbolAndSignals; 22 23 static bool runOnCode(tooling::ToolAction *ToolAction, StringRef Code, 24 StringRef FileName, 25 const std::vector<std::string> &ExtraArgs) { 26 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 27 new llvm::vfs::InMemoryFileSystem); 28 llvm::IntrusiveRefCntPtr<FileManager> Files( 29 new FileManager(FileSystemOptions(), InMemoryFileSystem)); 30 // FIXME: Investigate why -fms-compatibility breaks tests. 31 std::vector<std::string> Args = {"include_fixer", "-fsyntax-only", 32 "-fno-ms-compatibility", 33 std::string(FileName)}; 34 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end()); 35 tooling::ToolInvocation Invocation( 36 Args, ToolAction, Files.get(), 37 std::make_shared<PCHContainerOperations>()); 38 39 InMemoryFileSystem->addFile(FileName, 0, 40 llvm::MemoryBuffer::getMemBuffer(Code)); 41 42 InMemoryFileSystem->addFile("foo.h", 0, 43 llvm::MemoryBuffer::getMemBuffer("\n")); 44 InMemoryFileSystem->addFile("dir/bar.h", 0, 45 llvm::MemoryBuffer::getMemBuffer("\n")); 46 InMemoryFileSystem->addFile("dir/otherdir/qux.h", 0, 47 llvm::MemoryBuffer::getMemBuffer("\n")); 48 InMemoryFileSystem->addFile("header.h", 0, 49 llvm::MemoryBuffer::getMemBuffer("bar b;")); 50 return Invocation.run(); 51 } 52 53 static std::string runIncludeFixer( 54 StringRef Code, 55 const std::vector<std::string> &ExtraArgs = std::vector<std::string>()) { 56 std::vector<SymbolAndSignals> Symbols = { 57 {SymbolInfo("string", SymbolInfo::SymbolKind::Class, "<string>", 58 {{SymbolInfo::ContextType::Namespace, "std"}}), 59 SymbolInfo::Signals{}}, 60 {SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"", 61 {{SymbolInfo::ContextType::Namespace, "std"}}), 62 SymbolInfo::Signals{}}, 63 {SymbolInfo("foo", SymbolInfo::SymbolKind::Class, 64 "\"dir/otherdir/qux.h\"", 65 {{SymbolInfo::ContextType::Namespace, "b"}, 66 {SymbolInfo::ContextType::Namespace, "a"}}), 67 SymbolInfo::Signals{}}, 68 {SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"", 69 {{SymbolInfo::ContextType::Namespace, "b"}, 70 {SymbolInfo::ContextType::Namespace, "a"}}), 71 SymbolInfo::Signals{}}, 72 {SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar2.h\"", 73 {{SymbolInfo::ContextType::Namespace, "c"}, 74 {SymbolInfo::ContextType::Namespace, "a"}}), 75 SymbolInfo::Signals{}}, 76 {SymbolInfo("Green", SymbolInfo::SymbolKind::Class, "\"color.h\"", 77 {{SymbolInfo::ContextType::EnumDecl, "Color"}, 78 {SymbolInfo::ContextType::Namespace, "b"}, 79 {SymbolInfo::ContextType::Namespace, "a"}}), 80 SymbolInfo::Signals{}}, 81 {SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", 82 {{SymbolInfo::ContextType::Namespace, "__a"}, 83 {SymbolInfo::ContextType::Namespace, "a"}}), 84 SymbolInfo::Signals{/*Seen=*/2, 0}}, 85 {SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", 86 {{SymbolInfo::ContextType::Namespace, "a"}}), 87 SymbolInfo::Signals{/*Seen=*/2, 0}}, 88 {SymbolInfo("StrCat", SymbolInfo::SymbolKind::Class, "\"strcat.h\"", 89 {{SymbolInfo::ContextType::Namespace, "str"}}), 90 SymbolInfo::Signals{}}, 91 {SymbolInfo("str", SymbolInfo::SymbolKind::Class, "\"str.h\"", {}), 92 SymbolInfo::Signals{}}, 93 {SymbolInfo("foo2", SymbolInfo::SymbolKind::Class, "\"foo2.h\"", {}), 94 SymbolInfo::Signals{}}, 95 }; 96 auto SymbolIndexMgr = std::make_unique<SymbolIndexManager>(); 97 SymbolIndexMgr->addSymbolIndex( 98 [=]() { return std::make_unique<InMemorySymbolIndex>(Symbols); }); 99 100 std::vector<IncludeFixerContext> FixerContexts; 101 IncludeFixerActionFactory Factory(*SymbolIndexMgr, FixerContexts, "llvm"); 102 std::string FakeFileName = "input.cc"; 103 runOnCode(&Factory, Code, FakeFileName, ExtraArgs); 104 assert(FixerContexts.size() == 1); 105 if (FixerContexts.front().getHeaderInfos().empty()) 106 return std::string(Code); 107 auto Replaces = createIncludeFixerReplacements(Code, FixerContexts.front()); 108 EXPECT_TRUE(static_cast<bool>(Replaces)) 109 << llvm::toString(Replaces.takeError()) << "\n"; 110 if (!Replaces) 111 return ""; 112 RewriterTestContext Context; 113 FileID ID = Context.createInMemoryFile(FakeFileName, Code); 114 tooling::applyAllReplacements(*Replaces, Context.Rewrite); 115 return Context.getRewrittenText(ID); 116 } 117 118 TEST(IncludeFixer, Typo) { 119 EXPECT_EQ("#include <string>\nstd::string foo;\n", 120 runIncludeFixer("std::string foo;\n")); 121 122 EXPECT_EQ("// comment\n#include \"foo.h\"\n#include <string>\n" 123 "std::string foo;\n#include \"dir/bar.h\"\n", 124 runIncludeFixer("// comment\n#include \"foo.h\"\nstd::string foo;\n" 125 "#include \"dir/bar.h\"\n")); 126 127 EXPECT_EQ("#include \"foo.h\"\n#include <string>\nstd::string foo;\n", 128 runIncludeFixer("#include \"foo.h\"\nstd::string foo;\n")); 129 130 EXPECT_EQ( 131 "#include \"foo.h\"\n#include <string>\nstd::string::size_type foo;\n", 132 runIncludeFixer("#include \"foo.h\"\nstd::string::size_type foo;\n")); 133 134 EXPECT_EQ("#include <string>\nstd::string foo;\n", 135 runIncludeFixer("string foo;\n")); 136 137 // Should not match std::string. 138 EXPECT_EQ("::string foo;\n", runIncludeFixer("::string foo;\n")); 139 } 140 141 TEST(IncludeFixer, IncompleteType) { 142 EXPECT_EQ( 143 "#include \"foo.h\"\n#include <string>\n" 144 "namespace std {\nclass string;\n}\nstd::string foo;\n", 145 runIncludeFixer("#include \"foo.h\"\n" 146 "namespace std {\nclass string;\n}\nstring foo;\n")); 147 148 EXPECT_EQ("#include <string>\n" 149 "class string;\ntypedef string foo;\nfoo f;\n", 150 runIncludeFixer("class string;\ntypedef string foo;\nfoo f;\n")); 151 } 152 153 TEST(IncludeFixer, MinimizeInclude) { 154 std::vector<std::string> IncludePath = {"-Idir/"}; 155 EXPECT_EQ("#include \"otherdir/qux.h\"\na::b::foo bar;\n", 156 runIncludeFixer("a::b::foo bar;\n", IncludePath)); 157 158 IncludePath = {"-isystemdir"}; 159 EXPECT_EQ("#include <otherdir/qux.h>\na::b::foo bar;\n", 160 runIncludeFixer("a::b::foo bar;\n", IncludePath)); 161 162 IncludePath = {"-iquotedir"}; 163 EXPECT_EQ("#include \"otherdir/qux.h\"\na::b::foo bar;\n", 164 runIncludeFixer("a::b::foo bar;\n", IncludePath)); 165 166 IncludePath = {"-Idir", "-Idir/otherdir"}; 167 EXPECT_EQ("#include \"qux.h\"\na::b::foo bar;\n", 168 runIncludeFixer("a::b::foo bar;\n", IncludePath)); 169 } 170 171 TEST(IncludeFixer, NestedName) { 172 EXPECT_EQ("#include \"dir/otherdir/qux.h\"\n" 173 "int x = a::b::foo(0);\n", 174 runIncludeFixer("int x = a::b::foo(0);\n")); 175 176 // FIXME: Handle simple macros. 177 EXPECT_EQ("#define FOO a::b::foo\nint x = FOO;\n", 178 runIncludeFixer("#define FOO a::b::foo\nint x = FOO;\n")); 179 EXPECT_EQ("#define FOO(x) a::##x\nint x = FOO(b::foo);\n", 180 runIncludeFixer("#define FOO(x) a::##x\nint x = FOO(b::foo);\n")); 181 182 // The empty namespace is cleaned up by clang-format after clang-include-fixer 183 // finishes. 184 EXPECT_EQ("#include \"dir/otherdir/qux.h\"\n" 185 "\nint a = a::b::foo(0);\n", 186 runIncludeFixer("namespace a {}\nint a = a::b::foo(0);\n")); 187 } 188 189 TEST(IncludeFixer, MultipleMissingSymbols) { 190 EXPECT_EQ("#include <string>\nstd::string bar;\nstd::sting foo;\n", 191 runIncludeFixer("std::string bar;\nstd::sting foo;\n")); 192 } 193 194 TEST(IncludeFixer, ScopedNamespaceSymbols) { 195 EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar b;\n}", 196 runIncludeFixer("namespace a {\nb::bar b;\n}")); 197 EXPECT_EQ("#include \"bar.h\"\nnamespace A {\na::b::bar b;\n}", 198 runIncludeFixer("namespace A {\na::b::bar b;\n}")); 199 EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nvoid func() { b::bar b; }\n} " 200 "// namespace a", 201 runIncludeFixer("namespace a {\nvoid func() { b::bar b; }\n}")); 202 EXPECT_EQ("namespace A { c::b::bar b; }\n", 203 runIncludeFixer("namespace A { c::b::bar b; }\n")); 204 // FIXME: The header should not be added here. Remove this after we support 205 // full match. 206 EXPECT_EQ("#include \"bar.h\"\nnamespace A {\na::b::bar b;\n}", 207 runIncludeFixer("namespace A {\nb::bar b;\n}")); 208 209 // Finds candidates for "str::StrCat". 210 EXPECT_EQ("#include \"strcat.h\"\nnamespace foo2 {\nstr::StrCat b;\n}", 211 runIncludeFixer("namespace foo2 {\nstr::StrCat b;\n}")); 212 // str::StrCat2 doesn't exist. 213 // In these two cases, StrCat2 is a nested class of class str. 214 EXPECT_EQ("#include \"str.h\"\nnamespace foo2 {\nstr::StrCat2 b;\n}", 215 runIncludeFixer("namespace foo2 {\nstr::StrCat2 b;\n}")); 216 EXPECT_EQ("#include \"str.h\"\nnamespace ns {\nstr::StrCat2 b;\n}", 217 runIncludeFixer("namespace ns {\nstr::StrCat2 b;\n}")); 218 } 219 220 TEST(IncludeFixer, EnumConstantSymbols) { 221 EXPECT_EQ("#include \"color.h\"\nint test = a::b::Green;\n", 222 runIncludeFixer("int test = a::b::Green;\n")); 223 } 224 225 TEST(IncludeFixer, IgnoreSymbolFromHeader) { 226 std::string Code = "#include \"header.h\""; 227 EXPECT_EQ(Code, runIncludeFixer(Code)); 228 } 229 230 // FIXME: add test cases for inserting and sorting multiple headers when 231 // clang-include-fixer supports multiple headers insertion. 232 TEST(IncludeFixer, InsertAndSortSingleHeader) { 233 // Insert one header. 234 std::string Code = "#include \"a.h\"\n" 235 "#include \"foo.h\"\n" 236 "\n" 237 "namespace a {\nb::bar b;\n}\n"; 238 std::string Expected = "#include \"a.h\"\n" 239 "#include \"bar.h\"\n" 240 "#include \"foo.h\"\n" 241 "\n" 242 "namespace a {\nb::bar b;\n}\n"; 243 EXPECT_EQ(Expected, runIncludeFixer(Code)); 244 } 245 246 TEST(IncludeFixer, DoNotDeleteMatchedSymbol) { 247 EXPECT_EQ("#include \"Vector.h\"\na::Vector v;", 248 runIncludeFixer("a::Vector v;")); 249 } 250 251 TEST(IncludeFixer, FixNamespaceQualifiers) { 252 EXPECT_EQ("#include \"bar.h\"\na::b::bar b;\n", 253 runIncludeFixer("b::bar b;\n")); 254 EXPECT_EQ("#include \"bar.h\"\na::b::bar b;\n", 255 runIncludeFixer("a::b::bar b;\n")); 256 EXPECT_EQ("#include \"bar.h\"\na::b::bar b;\n", 257 runIncludeFixer("bar b;\n")); 258 EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar b;\n}\n", 259 runIncludeFixer("namespace a {\nb::bar b;\n}\n")); 260 EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar b;\n}\n", 261 runIncludeFixer("namespace a {\nbar b;\n}\n")); 262 EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nnamespace b{\nbar b;\n}\n} " 263 "// namespace a\n", 264 runIncludeFixer("namespace a {\nnamespace b{\nbar b;\n}\n}\n")); 265 EXPECT_EQ("c::b::bar b;\n", 266 runIncludeFixer("c::b::bar b;\n")); 267 EXPECT_EQ("#include \"bar.h\"\nnamespace d {\na::b::bar b;\n}\n", 268 runIncludeFixer("namespace d {\nbar b;\n}\n")); 269 EXPECT_EQ("#include \"bar2.h\"\nnamespace c {\na::c::bar b;\n}\n", 270 runIncludeFixer("namespace c {\nbar b;\n}\n")); 271 272 // Test common qualifiers reduction. 273 EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nnamespace d {\nb::bar b;\n}\n} " 274 "// namespace a\n", 275 runIncludeFixer("namespace a {\nnamespace d {\nbar b;\n}\n}\n")); 276 EXPECT_EQ("#include \"bar.h\"\nnamespace d {\nnamespace a {\na::b::bar " 277 "b;\n}\n} // namespace d\n", 278 runIncludeFixer("namespace d {\nnamespace a {\nbar b;\n}\n}\n")); 279 280 // Test nested classes. 281 EXPECT_EQ("#include \"bar.h\"\nnamespace d {\na::b::bar::t b;\n}\n", 282 runIncludeFixer("namespace d {\nbar::t b;\n}\n")); 283 EXPECT_EQ("#include \"bar.h\"\nnamespace c {\na::b::bar::t b;\n}\n", 284 runIncludeFixer("namespace c {\nbar::t b;\n}\n")); 285 EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar::t b;\n}\n", 286 runIncludeFixer("namespace a {\nbar::t b;\n}\n")); 287 288 EXPECT_EQ("#include \"color.h\"\nint test = a::b::Green;\n", 289 runIncludeFixer("int test = Green;\n")); 290 EXPECT_EQ("#include \"color.h\"\nnamespace d {\nint test = a::b::Green;\n}\n", 291 runIncludeFixer("namespace d {\nint test = Green;\n}\n")); 292 EXPECT_EQ("#include \"color.h\"\nnamespace a {\nint test = b::Green;\n}\n", 293 runIncludeFixer("namespace a {\nint test = Green;\n}\n")); 294 295 // Test global scope operator. 296 EXPECT_EQ("#include \"bar.h\"\n::a::b::bar b;\n", 297 runIncludeFixer("::a::b::bar b;\n")); 298 EXPECT_EQ("#include \"bar.h\"\nnamespace a {\n::a::b::bar b;\n}\n", 299 runIncludeFixer("namespace a {\n::a::b::bar b;\n}\n")); 300 } 301 302 TEST(IncludeFixer, FixNamespaceQualifiersForAllInstances) { 303 const char TestCode[] = R"( 304 namespace a { 305 bar b; 306 int func1() { 307 bar a; 308 bar *p = new bar(); 309 return 0; 310 } 311 } // namespace a 312 313 namespace a { 314 bar func2() { 315 bar f; 316 return f; 317 } 318 } // namespace a 319 320 // Non-fixed cases: 321 void f() { 322 bar b; 323 } 324 325 namespace a { 326 namespace c { 327 bar b; 328 } // namespace c 329 } // namespace a 330 )"; 331 332 const char ExpectedCode[] = R"( 333 #include "bar.h" 334 namespace a { 335 b::bar b; 336 int func1() { 337 b::bar a; 338 b::bar *p = new b::bar(); 339 return 0; 340 } 341 } // namespace a 342 343 namespace a { 344 b::bar func2() { 345 b::bar f; 346 return f; 347 } 348 } // namespace a 349 350 // Non-fixed cases: 351 void f() { 352 bar b; 353 } 354 355 namespace a { 356 namespace c { 357 bar b; 358 } // namespace c 359 } // namespace a 360 )"; 361 362 EXPECT_EQ(ExpectedCode, runIncludeFixer(TestCode)); 363 } 364 365 TEST(IncludeFixer, DontAddQualifiersForMissingCompleteType) { 366 EXPECT_EQ("#include \"bar.h\"\nclass bar;\nvoid f() {\nbar* b;\nb->f();\n}", 367 runIncludeFixer("class bar;\nvoid f() {\nbar* b;\nb->f();\n}")); 368 } 369 370 } // namespace 371 } // namespace include_fixer 372 } // namespace clang 373