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