clang-tools 20.0.0git
HeadersTests.cpp
Go to the documentation of this file.
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
29namespace clang {
30namespace clangd {
31namespace {
32
33using ::testing::AllOf;
34using ::testing::Contains;
35using ::testing::ElementsAre;
36using ::testing::IsEmpty;
37using ::testing::Not;
38using ::testing::UnorderedElementsAre;
39
40class HeadersTest : public ::testing::Test {
41public:
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
49private:
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);
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
71protected:
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
149MATCHER_P(written, Name, "") { return arg.Written == Name; }
150MATCHER_P(resolved, Name, "") { return arg.Resolved == Name; }
151MATCHER_P(includeLine, N, "") { return arg.HashLine == N; }
152MATCHER_P(directive, D, "") { return arg.Directive == D; }
153
154MATCHER_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
162TEST_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
178TEST_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
202TEST_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
232TEST_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
248TEST_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
258TEST_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
283TEST_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
298TEST_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
307TEST_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
316TEST_F(HeadersTest, DoNotInsertIfInSameFile) {
317 MainFile = testPath("main.h");
318 EXPECT_EQ(calculate(MainFile), "");
319}
320
321TEST_F(HeadersTest, DoNotInsertOffIncludePath) {
322 MainFile = testPath("sub/main.cpp");
323 EXPECT_EQ(calculate(testPath("sub2/main.cpp")), "");
324}
325
326TEST_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
336TEST_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
347TEST_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
353TEST_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
365TEST_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
373TEST_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
382TEST_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
392TEST(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
414TEST_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
llvm::SmallString< 256U > Name
std::string Subdir
IgnoringDiagConsumer IgnoreDiags
std::unique_ptr< CompilerInstance > Clang
std::vector< std::function< bool(llvm::StringRef)> > AngledHeaders
std::vector< std::function< bool(llvm::StringRef)> > QuotedHeaders
std::string MainFile
std::string SearchDirArg
std::string Filename
Filename as a string.
const MacroDirective * Directive
FieldAction Action
std::vector< HeaderEntry > HeaderContents
Definition: Modularize.cpp:482
std::vector< const char * > Expected
std::unique_ptr< CompilerInvocation > CI
llvm::SmallVector< const Inclusion * > mainFileIncludesWithSpelling(llvm::StringRef Spelling) const
Definition: Headers.cpp:256
llvm::DenseMap< HeaderID, unsigned > includeDepth(HeaderID Root=MainFileID) const
Definition: Headers.cpp:228
std::vector< Inclusion > MainFileIncludes
Definition: Headers.h:176
llvm::DenseMap< HeaderID, SmallVector< HeaderID > > IncludeChildren
Definition: Headers.h:171
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
Definition: Compiler.cpp:95
MATCHER_P2(hasFlag, Flag, Path, "")
MATCHER_P(named, N, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:93
TEST(BackgroundQueueTest, Priority)
std::unique_ptr< CompilerInstance > prepareCompilerInstance(std::unique_ptr< clang::CompilerInvocation > CI, const PrecompiledPreamble *Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, DiagnosticConsumer &DiagsClient)
Definition: Compiler.cpp:129
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:29
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:36