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