1 //===-- HeadersTests.cpp - Include headers unit tests -----------*- C++ -*-===// 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 "Headers.h" 10 11 #include "Compiler.h" 12 #include "Matchers.h" 13 #include "TestFS.h" 14 #include "TestTU.h" 15 #include "clang/Basic/TokenKinds.h" 16 #include "clang/Frontend/CompilerInstance.h" 17 #include "clang/Frontend/CompilerInvocation.h" 18 #include "clang/Frontend/FrontendActions.h" 19 #include "clang/Tooling/Inclusions/HeaderIncludes.h" 20 #include "llvm/ADT/StringRef.h" 21 #include "llvm/Support/Error.h" 22 #include "llvm/Support/FormatVariadic.h" 23 #include "llvm/Support/Path.h" 24 #include "llvm/Testing/Support/Error.h" 25 #include "gmock/gmock.h" 26 #include "gtest/gtest.h" 27 #include <optional> 28 29 namespace clang { 30 namespace clangd { 31 namespace { 32 33 using ::testing::AllOf; 34 using ::testing::Contains; 35 using ::testing::ElementsAre; 36 using ::testing::IsEmpty; 37 using ::testing::Not; 38 using ::testing::UnorderedElementsAre; 39 40 class HeadersTest : public ::testing::Test { 41 public: 42 HeadersTest() { 43 CDB.ExtraClangFlags = {SearchDirArg.c_str()}; 44 FS.Files[MainFile] = ""; 45 // Make sure directory sub/ exists. 46 FS.Files[testPath("sub/EMPTY")] = ""; 47 } 48 49 private: 50 std::unique_ptr<CompilerInstance> setupClang() { 51 auto Cmd = CDB.getCompileCommand(MainFile); 52 assert(static_cast<bool>(Cmd)); 53 54 ParseInputs PI; 55 PI.CompileCommand = *Cmd; 56 PI.TFS = &FS; 57 auto CI = buildCompilerInvocation(PI, IgnoreDiags); 58 EXPECT_TRUE(static_cast<bool>(CI)); 59 // The diagnostic options must be set before creating a CompilerInstance. 60 CI->getDiagnosticOpts().IgnoreWarnings = true; 61 auto VFS = PI.TFS->view(Cmd->Directory); 62 auto Clang = prepareCompilerInstance( 63 std::move(CI), /*Preamble=*/nullptr, 64 llvm::MemoryBuffer::getMemBuffer(FS.Files[MainFile], MainFile), 65 std::move(VFS), IgnoreDiags); 66 67 EXPECT_FALSE(Clang->getFrontendOpts().Inputs.empty()); 68 return Clang; 69 } 70 71 protected: 72 IncludeStructure::HeaderID getID(StringRef Filename, 73 IncludeStructure &Includes) { 74 auto &SM = Clang->getSourceManager(); 75 auto Entry = SM.getFileManager().getFileRef(Filename); 76 EXPECT_THAT_EXPECTED(Entry, llvm::Succeeded()); 77 return Includes.getOrCreateID(*Entry); 78 } 79 80 IncludeStructure collectIncludes() { 81 Clang = setupClang(); 82 PreprocessOnlyAction Action; 83 EXPECT_TRUE( 84 Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])); 85 IncludeStructure Includes; 86 Includes.collect(*Clang); 87 EXPECT_FALSE(Action.Execute()); 88 Action.EndSourceFile(); 89 return Includes; 90 } 91 92 // Calculates the include path, or returns "" on error or header should not be 93 // inserted. 94 std::string calculate(PathRef Original, PathRef Preferred = "", 95 const std::vector<Inclusion> &Inclusions = {}) { 96 Clang = setupClang(); 97 PreprocessOnlyAction Action; 98 EXPECT_TRUE( 99 Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])); 100 101 if (Preferred.empty()) 102 Preferred = Original; 103 auto ToHeaderFile = [](llvm::StringRef Header) { 104 return HeaderFile{std::string(Header), 105 /*Verbatim=*/!llvm::sys::path::is_absolute(Header)}; 106 }; 107 108 IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(), 109 CDB.getCompileCommand(MainFile)->Directory, 110 &Clang->getPreprocessor().getHeaderSearchInfo(), 111 QuotedHeaders, AngledHeaders); 112 for (const auto &Inc : Inclusions) 113 Inserter.addExisting(Inc); 114 auto Inserted = ToHeaderFile(Preferred); 115 if (!Inserter.shouldInsertInclude(Original, Inserted)) 116 return ""; 117 auto Path = Inserter.calculateIncludePath(Inserted, MainFile); 118 Action.EndSourceFile(); 119 return Path.value_or(""); 120 } 121 122 std::optional<TextEdit> insert(llvm::StringRef VerbatimHeader, 123 tooling::IncludeDirective Directive) { 124 Clang = setupClang(); 125 PreprocessOnlyAction Action; 126 EXPECT_TRUE( 127 Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])); 128 129 IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(), 130 CDB.getCompileCommand(MainFile)->Directory, 131 &Clang->getPreprocessor().getHeaderSearchInfo(), 132 QuotedHeaders, AngledHeaders); 133 auto Edit = Inserter.insert(VerbatimHeader, Directive); 134 Action.EndSourceFile(); 135 return Edit; 136 } 137 138 MockFS FS; 139 MockCompilationDatabase CDB; 140 std::string MainFile = testPath("main.cpp"); 141 std::string Subdir = testPath("sub"); 142 std::string SearchDirArg = (llvm::Twine("-I") + Subdir).str(); 143 IgnoringDiagConsumer IgnoreDiags; 144 std::vector<std::function<bool(llvm::StringRef)>> QuotedHeaders; 145 std::vector<std::function<bool(llvm::StringRef)>> AngledHeaders; 146 std::unique_ptr<CompilerInstance> Clang; 147 }; 148 149 MATCHER_P(written, Name, "") { return arg.Written == Name; } 150 MATCHER_P(resolved, Name, "") { return arg.Resolved == Name; } 151 MATCHER_P(includeLine, N, "") { return arg.HashLine == N; } 152 MATCHER_P(directive, D, "") { return arg.Directive == D; } 153 154 MATCHER_P2(Distance, File, D, "") { 155 if (arg.getFirst() != File) 156 *result_listener << "file =" << static_cast<unsigned>(arg.getFirst()); 157 if (arg.getSecond() != D) 158 *result_listener << "distance =" << arg.getSecond(); 159 return arg.getFirst() == File && arg.getSecond() == D; 160 } 161 162 TEST_F(HeadersTest, CollectRewrittenAndResolved) { 163 FS.Files[MainFile] = R"cpp( 164 #include "sub/bar.h" // not shortest 165 )cpp"; 166 std::string BarHeader = testPath("sub/bar.h"); 167 FS.Files[BarHeader] = ""; 168 169 auto Includes = collectIncludes(); 170 EXPECT_THAT(Includes.MainFileIncludes, 171 UnorderedElementsAre( 172 AllOf(written("\"sub/bar.h\""), resolved(BarHeader)))); 173 EXPECT_THAT(Includes.includeDepth(getID(MainFile, Includes)), 174 UnorderedElementsAre(Distance(getID(MainFile, Includes), 0u), 175 Distance(getID(BarHeader, Includes), 1u))); 176 } 177 178 TEST_F(HeadersTest, OnlyCollectInclusionsInMain) { 179 std::string BazHeader = testPath("sub/baz.h"); 180 FS.Files[BazHeader] = ""; 181 std::string BarHeader = testPath("sub/bar.h"); 182 FS.Files[BarHeader] = R"cpp( 183 #include "baz.h" 184 )cpp"; 185 FS.Files[MainFile] = R"cpp( 186 #include "bar.h" 187 )cpp"; 188 auto Includes = collectIncludes(); 189 EXPECT_THAT( 190 Includes.MainFileIncludes, 191 UnorderedElementsAre(AllOf(written("\"bar.h\""), resolved(BarHeader)))); 192 EXPECT_THAT(Includes.includeDepth(getID(MainFile, Includes)), 193 UnorderedElementsAre(Distance(getID(MainFile, Includes), 0u), 194 Distance(getID(BarHeader, Includes), 1u), 195 Distance(getID(BazHeader, Includes), 2u))); 196 // includeDepth() also works for non-main files. 197 EXPECT_THAT(Includes.includeDepth(getID(BarHeader, Includes)), 198 UnorderedElementsAre(Distance(getID(BarHeader, Includes), 0u), 199 Distance(getID(BazHeader, Includes), 1u))); 200 } 201 202 TEST_F(HeadersTest, CacheBySpellingIsBuiltForMainInclusions) { 203 std::string FooHeader = testPath("foo.h"); 204 FS.Files[FooHeader] = R"cpp( 205 void foo(); 206 )cpp"; 207 std::string BarHeader = testPath("bar.h"); 208 FS.Files[BarHeader] = R"cpp( 209 void bar(); 210 )cpp"; 211 std::string BazHeader = testPath("baz.h"); 212 FS.Files[BazHeader] = R"cpp( 213 void baz(); 214 )cpp"; 215 FS.Files[MainFile] = R"cpp( 216 #include "foo.h" 217 #include "bar.h" 218 #include "baz.h" 219 )cpp"; 220 auto Includes = collectIncludes(); 221 EXPECT_THAT(Includes.MainFileIncludes, 222 UnorderedElementsAre(written("\"foo.h\""), written("\"bar.h\""), 223 written("\"baz.h\""))); 224 EXPECT_THAT(Includes.mainFileIncludesWithSpelling("\"foo.h\""), 225 UnorderedElementsAre(&Includes.MainFileIncludes[0])); 226 EXPECT_THAT(Includes.mainFileIncludesWithSpelling("\"bar.h\""), 227 UnorderedElementsAre(&Includes.MainFileIncludes[1])); 228 EXPECT_THAT(Includes.mainFileIncludesWithSpelling("\"baz.h\""), 229 UnorderedElementsAre(&Includes.MainFileIncludes[2])); 230 } 231 232 TEST_F(HeadersTest, PreambleIncludesPresentOnce) { 233 // We use TestTU here, to ensure we use the preamble replay logic. 234 // We're testing that the logic doesn't crash, and doesn't result in duplicate 235 // includes. (We'd test more directly, but it's pretty well encapsulated!) 236 auto TU = TestTU::withCode(R"cpp( 237 #include "a.h" 238 239 #include "a.h" 240 void foo(); 241 #include "a.h" 242 )cpp"); 243 TU.HeaderFilename = "a.h"; // suppress "not found". 244 EXPECT_THAT(TU.build().getIncludeStructure().MainFileIncludes, 245 ElementsAre(includeLine(1), includeLine(3), includeLine(5))); 246 } 247 248 TEST_F(HeadersTest, UnResolvedInclusion) { 249 FS.Files[MainFile] = R"cpp( 250 #include "foo.h" 251 )cpp"; 252 253 EXPECT_THAT(collectIncludes().MainFileIncludes, 254 UnorderedElementsAre(AllOf(written("\"foo.h\""), resolved("")))); 255 EXPECT_THAT(collectIncludes().IncludeChildren, IsEmpty()); 256 } 257 258 TEST_F(HeadersTest, IncludedFilesGraph) { 259 FS.Files[MainFile] = R"cpp( 260 #include "bar.h" 261 #include "foo.h" 262 )cpp"; 263 std::string BarHeader = testPath("bar.h"); 264 FS.Files[BarHeader] = ""; 265 std::string FooHeader = testPath("foo.h"); 266 FS.Files[FooHeader] = R"cpp( 267 #include "bar.h" 268 #include "baz.h" 269 )cpp"; 270 std::string BazHeader = testPath("baz.h"); 271 FS.Files[BazHeader] = ""; 272 273 auto Includes = collectIncludes(); 274 llvm::DenseMap<IncludeStructure::HeaderID, 275 SmallVector<IncludeStructure::HeaderID>> 276 Expected = {{getID(MainFile, Includes), 277 {getID(BarHeader, Includes), getID(FooHeader, Includes)}}, 278 {getID(FooHeader, Includes), 279 {getID(BarHeader, Includes), getID(BazHeader, Includes)}}}; 280 EXPECT_EQ(Includes.IncludeChildren, Expected); 281 } 282 283 TEST_F(HeadersTest, IncludeDirective) { 284 FS.Files[MainFile] = R"cpp( 285 #include "foo.h" 286 #import "foo.h" 287 #include_next "foo.h" 288 )cpp"; 289 290 // ms-compatibility changes meaning of #import, make sure it is turned off. 291 CDB.ExtraClangFlags.push_back("-fno-ms-compatibility"); 292 EXPECT_THAT(collectIncludes().MainFileIncludes, 293 UnorderedElementsAre(directive(tok::pp_include), 294 directive(tok::pp_import), 295 directive(tok::pp_include_next))); 296 } 297 298 TEST_F(HeadersTest, SearchPath) { 299 FS.Files["foo/bar.h"] = "x"; 300 FS.Files["foo/bar/baz.h"] = "y"; 301 CDB.ExtraClangFlags.push_back("-Ifoo/bar"); 302 CDB.ExtraClangFlags.push_back("-Ifoo/bar/.."); 303 EXPECT_THAT(collectIncludes().SearchPathsCanonical, 304 ElementsAre(Subdir, testPath("foo/bar"), testPath("foo"))); 305 } 306 307 TEST_F(HeadersTest, InsertInclude) { 308 std::string Path = testPath("sub/bar.h"); 309 FS.Files[Path] = ""; 310 EXPECT_EQ(calculate(Path), "\"bar.h\""); 311 312 AngledHeaders.push_back([](auto Path) { return true; }); 313 EXPECT_EQ(calculate(Path), "<bar.h>"); 314 } 315 316 TEST_F(HeadersTest, DoNotInsertIfInSameFile) { 317 MainFile = testPath("main.h"); 318 EXPECT_EQ(calculate(MainFile), ""); 319 } 320 321 TEST_F(HeadersTest, DoNotInsertOffIncludePath) { 322 MainFile = testPath("sub/main.cpp"); 323 EXPECT_EQ(calculate(testPath("sub2/main.cpp")), ""); 324 } 325 326 TEST_F(HeadersTest, ShortenIncludesInSearchPath) { 327 std::string BarHeader = testPath("sub/bar.h"); 328 EXPECT_EQ(calculate(BarHeader), "\"bar.h\""); 329 330 SearchDirArg = (llvm::Twine("-I") + Subdir + "/..").str(); 331 CDB.ExtraClangFlags = {SearchDirArg.c_str()}; 332 BarHeader = testPath("sub/bar.h"); 333 EXPECT_EQ(calculate(BarHeader), "\"sub/bar.h\""); 334 } 335 336 TEST_F(HeadersTest, ShortenIncludesInSearchPathBracketed) { 337 AngledHeaders.push_back([](auto Path) { return true; }); 338 std::string BarHeader = testPath("sub/bar.h"); 339 EXPECT_EQ(calculate(BarHeader), "<bar.h>"); 340 341 SearchDirArg = (llvm::Twine("-I") + Subdir + "/..").str(); 342 CDB.ExtraClangFlags = {SearchDirArg.c_str()}; 343 BarHeader = testPath("sub/bar.h"); 344 EXPECT_EQ(calculate(BarHeader), "<sub/bar.h>"); 345 } 346 347 TEST_F(HeadersTest, ShortenedIncludeNotInSearchPath) { 348 std::string BarHeader = 349 llvm::sys::path::convert_to_slash(testPath("sub-2/bar.h")); 350 EXPECT_EQ(calculate(BarHeader, ""), "\"sub-2/bar.h\""); 351 } 352 353 TEST_F(HeadersTest, PreferredHeader) { 354 std::string BarHeader = testPath("sub/bar.h"); 355 EXPECT_EQ(calculate(BarHeader, "<bar>"), "<bar>"); 356 357 std::string BazHeader = testPath("sub/baz.h"); 358 EXPECT_EQ(calculate(BarHeader, BazHeader), "\"baz.h\""); 359 360 AngledHeaders.push_back([](auto Path) { return true; }); 361 std::string BiffHeader = testPath("sub/biff.h"); 362 EXPECT_EQ(calculate(BarHeader, BiffHeader), "<biff.h>"); 363 } 364 365 TEST_F(HeadersTest, DontInsertDuplicatePreferred) { 366 Inclusion Inc; 367 Inc.Written = "\"bar.h\""; 368 Inc.Resolved = ""; 369 EXPECT_EQ(calculate(testPath("sub/bar.h"), "\"bar.h\"", {Inc}), ""); 370 EXPECT_EQ(calculate("\"x.h\"", "\"bar.h\"", {Inc}), ""); 371 } 372 373 TEST_F(HeadersTest, DontInsertDuplicateResolved) { 374 Inclusion Inc; 375 Inc.Written = "fake-bar.h"; 376 Inc.Resolved = testPath("sub/bar.h"); 377 EXPECT_EQ(calculate(Inc.Resolved, "", {Inc}), ""); 378 // Do not insert preferred. 379 EXPECT_EQ(calculate(Inc.Resolved, "\"BAR.h\"", {Inc}), ""); 380 } 381 382 TEST_F(HeadersTest, PreferInserted) { 383 auto Edit = insert("<y>", tooling::IncludeDirective::Include); 384 ASSERT_TRUE(Edit); 385 EXPECT_EQ(Edit->newText, "#include <y>\n"); 386 387 Edit = insert("\"header.h\"", tooling::IncludeDirective::Import); 388 ASSERT_TRUE(Edit); 389 EXPECT_EQ(Edit->newText, "#import \"header.h\"\n"); 390 } 391 392 TEST(Headers, NoHeaderSearchInfo) { 393 std::string MainFile = testPath("main.cpp"); 394 IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(), 395 /*BuildDir=*/"", /*HeaderSearchInfo=*/nullptr, 396 /*QuotedHeaders=*/{}, /*AngledHeaders=*/{}); 397 398 auto HeaderPath = testPath("sub/bar.h"); 399 auto Inserting = HeaderFile{HeaderPath, /*Verbatim=*/false}; 400 auto Verbatim = HeaderFile{"<x>", /*Verbatim=*/true}; 401 402 EXPECT_EQ(Inserter.calculateIncludePath(Inserting, MainFile), 403 std::string("\"sub/bar.h\"")); 404 EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Inserting), false); 405 406 EXPECT_EQ(Inserter.calculateIncludePath(Verbatim, MainFile), 407 std::string("<x>")); 408 EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Verbatim), true); 409 410 EXPECT_EQ(Inserter.calculateIncludePath(Inserting, "sub2/main2.cpp"), 411 std::nullopt); 412 } 413 414 TEST_F(HeadersTest, PresumedLocations) { 415 std::string HeaderFile = "__preamble_patch__.h"; 416 417 // Line map inclusion back to main file. 418 std::string HeaderContents = 419 llvm::formatv("#line 0 \"{0}\"", llvm::sys::path::filename(MainFile)); 420 HeaderContents += R"cpp( 421 #line 3 422 #include <a.h>)cpp"; 423 FS.Files[HeaderFile] = HeaderContents; 424 425 // Including through non-builtin file has no effects. 426 FS.Files[MainFile] = "#include \"__preamble_patch__.h\"\n\n"; 427 EXPECT_THAT(collectIncludes().MainFileIncludes, 428 Not(Contains(written("<a.h>")))); 429 430 // Now include through built-in file. 431 CDB.ExtraClangFlags = {"-include", testPath(HeaderFile)}; 432 EXPECT_THAT(collectIncludes().MainFileIncludes, 433 Contains(AllOf(includeLine(2), written("<a.h>")))); 434 } 435 436 } // namespace 437 } // namespace clangd 438 } // namespace clang 439