clang-tools  14.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 "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 "clang/Lex/PreprocessorOptions.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 "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 
26 namespace clang {
27 namespace clangd {
28 namespace {
29 
30 using ::testing::AllOf;
31 using ::testing::Contains;
32 using ::testing::ElementsAre;
33 using ::testing::IsEmpty;
34 using ::testing::Not;
35 using ::testing::UnorderedElementsAre;
36 using ::testing::UnorderedElementsAreArray;
37 
38 class HeadersTest : public ::testing::Test {
39 public:
40  HeadersTest() {
41  CDB.ExtraClangFlags = {SearchDirArg.c_str()};
42  FS.Files[MainFile] = "";
43  // Make sure directory sub/ exists.
44  FS.Files[testPath("sub/EMPTY")] = "";
45  }
46 
47 private:
48  std::unique_ptr<CompilerInstance> setupClang() {
49  auto Cmd = CDB.getCompileCommand(MainFile);
50  assert(static_cast<bool>(Cmd));
51 
52  ParseInputs PI;
53  PI.CompileCommand = *Cmd;
54  PI.TFS = &FS;
56  EXPECT_TRUE(static_cast<bool>(CI));
57  // The diagnostic options must be set before creating a CompilerInstance.
58  CI->getDiagnosticOpts().IgnoreWarnings = true;
59  auto VFS = PI.TFS->view(Cmd->Directory);
61  std::move(CI), /*Preamble=*/nullptr,
62  llvm::MemoryBuffer::getMemBuffer(FS.Files[MainFile], MainFile),
63  std::move(VFS), IgnoreDiags);
64 
65  EXPECT_FALSE(Clang->getFrontendOpts().Inputs.empty());
66  return Clang;
67  }
68 
69 protected:
70  IncludeStructure::HeaderID getID(StringRef Filename,
71  IncludeStructure &Includes) {
72  auto Entry = Clang->getSourceManager().getFileManager().getFile(Filename);
73  EXPECT_TRUE(Entry);
74  return Includes.getOrCreateID(*Entry);
75  }
76 
77  IncludeStructure collectIncludes() {
78  Clang = setupClang();
79  PreprocessOnlyAction Action;
80  EXPECT_TRUE(
81  Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
82  IncludeStructure Includes;
83  Clang->getPreprocessor().addPPCallbacks(
84  collectIncludeStructureCallback(Clang->getSourceManager(), &Includes));
85  EXPECT_FALSE(Action.Execute());
86  Action.EndSourceFile();
87  return Includes;
88  }
89 
90  // Calculates the include path, or returns "" on error or header should not be
91  // inserted.
92  std::string calculate(PathRef Original, PathRef Preferred = "",
93  const std::vector<Inclusion> &Inclusions = {}) {
94  Clang = setupClang();
95  PreprocessOnlyAction Action;
96  EXPECT_TRUE(
97  Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
98 
99  if (Preferred.empty())
100  Preferred = Original;
101  auto ToHeaderFile = [](llvm::StringRef Header) {
102  return HeaderFile{std::string(Header),
103  /*Verbatim=*/!llvm::sys::path::is_absolute(Header)};
104  };
105 
106  IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(),
107  CDB.getCompileCommand(MainFile)->Directory,
108  &Clang->getPreprocessor().getHeaderSearchInfo());
109  for (const auto &Inc : Inclusions)
110  Inserter.addExisting(Inc);
111  auto Inserted = ToHeaderFile(Preferred);
112  if (!Inserter.shouldInsertInclude(Original, Inserted))
113  return "";
114  auto Path = Inserter.calculateIncludePath(Inserted, MainFile);
115  Action.EndSourceFile();
116  return Path.getValueOr("");
117  }
118 
119  llvm::Optional<TextEdit> insert(llvm::StringRef VerbatimHeader) {
120  Clang = setupClang();
121  PreprocessOnlyAction Action;
122  EXPECT_TRUE(
123  Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
124 
125  IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(),
126  CDB.getCompileCommand(MainFile)->Directory,
127  &Clang->getPreprocessor().getHeaderSearchInfo());
128  auto Edit = Inserter.insert(VerbatimHeader);
129  Action.EndSourceFile();
130  return Edit;
131  }
132 
133  MockFS FS;
134  MockCompilationDatabase CDB;
135  std::string MainFile = testPath("main.cpp");
136  std::string Subdir = testPath("sub");
137  std::string SearchDirArg = (llvm::Twine("-I") + Subdir).str();
138  IgnoringDiagConsumer IgnoreDiags;
139  std::unique_ptr<CompilerInstance> Clang;
140 };
141 
142 MATCHER_P(Written, Name, "") { return arg.Written == Name; }
143 MATCHER_P(Resolved, Name, "") { return arg.Resolved == Name; }
144 MATCHER_P(IncludeLine, N, "") { return arg.HashLine == N; }
145 MATCHER_P(Directive, D, "") { return arg.Directive == D; }
146 
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;
153 }
154 
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] = "";
161 
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)));
169 }
170 
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(Includes.MainFileIncludes,
183  UnorderedElementsAre(
184  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)));
193 }
194 
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"
201 
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)));
209 }
210 
211 TEST_F(HeadersTest, UnResolvedInclusion) {
212  FS.Files[MainFile] = R"cpp(
213 #include "foo.h"
214 )cpp";
215 
216  EXPECT_THAT(collectIncludes().MainFileIncludes,
217  UnorderedElementsAre(AllOf(Written("\"foo.h\""), Resolved(""))));
218  EXPECT_THAT(collectIncludes().IncludeChildren, IsEmpty());
219 }
220 
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] = "";
235 
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);
244 }
245 
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";
252 
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)));
259 }
260 
261 TEST_F(HeadersTest, InsertInclude) {
262  std::string Path = testPath("sub/bar.h");
263  FS.Files[Path] = "";
264  EXPECT_EQ(calculate(Path), "\"bar.h\"");
265 }
266 
267 TEST_F(HeadersTest, DoNotInsertIfInSameFile) {
268  MainFile = testPath("main.h");
269  EXPECT_EQ(calculate(MainFile), "");
270 }
271 
272 TEST_F(HeadersTest, DoNotInsertOffIncludePath) {
273  MainFile = testPath("sub/main.cpp");
274  EXPECT_EQ(calculate(testPath("sub2/main.cpp")), "");
275 }
276 
277 TEST_F(HeadersTest, ShortenIncludesInSearchPath) {
278  std::string BarHeader = testPath("sub/bar.h");
279  EXPECT_EQ(calculate(BarHeader), "\"bar.h\"");
280 
281  SearchDirArg = (llvm::Twine("-I") + Subdir + "/..").str();
282  CDB.ExtraClangFlags = {SearchDirArg.c_str()};
283  BarHeader = testPath("sub/bar.h");
284  EXPECT_EQ(calculate(BarHeader), "\"sub/bar.h\"");
285 }
286 
287 TEST_F(HeadersTest, ShortenedIncludeNotInSearchPath) {
288  std::string BarHeader =
289  llvm::sys::path::convert_to_slash(testPath("sub-2/bar.h"));
290  EXPECT_EQ(calculate(BarHeader, ""), "\"sub-2/bar.h\"");
291 }
292 
293 TEST_F(HeadersTest, PreferredHeader) {
294  std::string BarHeader = testPath("sub/bar.h");
295  EXPECT_EQ(calculate(BarHeader, "<bar>"), "<bar>");
296 
297  std::string BazHeader = testPath("sub/baz.h");
298  EXPECT_EQ(calculate(BarHeader, BazHeader), "\"baz.h\"");
299 }
300 
301 TEST_F(HeadersTest, DontInsertDuplicatePreferred) {
302  Inclusion Inc;
303  Inc.Written = "\"bar.h\"";
304  Inc.Resolved = "";
305  EXPECT_EQ(calculate(testPath("sub/bar.h"), "\"bar.h\"", {Inc}), "");
306  EXPECT_EQ(calculate("\"x.h\"", "\"bar.h\"", {Inc}), "");
307 }
308 
309 TEST_F(HeadersTest, DontInsertDuplicateResolved) {
310  Inclusion Inc;
311  Inc.Written = "fake-bar.h";
312  Inc.Resolved = testPath("sub/bar.h");
313  EXPECT_EQ(calculate(Inc.Resolved, "", {Inc}), "");
314  // Do not insert preferred.
315  EXPECT_EQ(calculate(Inc.Resolved, "\"BAR.h\"", {Inc}), "");
316 }
317 
318 TEST_F(HeadersTest, PreferInserted) {
319  auto Edit = insert("<y>");
320  EXPECT_TRUE(Edit.hasValue());
321  EXPECT_TRUE(StringRef(Edit->newText).contains("<y>"));
322 }
323 
324 TEST(Headers, NoHeaderSearchInfo) {
325  std::string MainFile = testPath("main.cpp");
326  IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(),
327  /*BuildDir=*/"", /*HeaderSearchInfo=*/nullptr);
328 
329  auto HeaderPath = testPath("sub/bar.h");
330  auto Inserting = HeaderFile{HeaderPath, /*Verbatim=*/false};
331  auto Verbatim = HeaderFile{"<x>", /*Verbatim=*/true};
332 
333  EXPECT_EQ(Inserter.calculateIncludePath(Inserting, MainFile),
334  std::string("\"sub/bar.h\""));
335  EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Inserting), false);
336 
337  EXPECT_EQ(Inserter.calculateIncludePath(Verbatim, MainFile),
338  std::string("<x>"));
339  EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Verbatim), true);
340 
341  EXPECT_EQ(Inserter.calculateIncludePath(Inserting, "sub2/main2.cpp"),
342  llvm::None);
343 }
344 
345 TEST_F(HeadersTest, PresumedLocations) {
346  std::string HeaderFile = "__preamble_patch__.h";
347 
348  // Line map inclusion back to main file.
349  std::string HeaderContents =
350  llvm::formatv("#line 0 \"{0}\"", llvm::sys::path::filename(MainFile));
351  HeaderContents += R"cpp(
352 #line 3
353 #include <a.h>)cpp";
354  FS.Files[HeaderFile] = HeaderContents;
355 
356  // Including through non-builtin file has no effects.
357  FS.Files[MainFile] = "#include \"__preamble_patch__.h\"\n\n";
358  EXPECT_THAT(collectIncludes().MainFileIncludes,
359  Not(Contains(Written("<a.h>"))));
360 
361  // Now include through built-in file.
362  CDB.ExtraClangFlags = {"-include", testPath(HeaderFile)};
363  EXPECT_THAT(collectIncludes().MainFileIncludes,
364  Contains(AllOf(IncludeLine(2), Written("<a.h>"))));
365 }
366 } // namespace
367 } // namespace clangd
368 } // namespace clang
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:751
Headers.h
clang::clangd::MATCHER_P2
MATCHER_P2(hasFlag, Flag, Path, "")
Definition: GlobalCompilationDatabaseTests.cpp:451
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:110
clang::clangd::testPath
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
Expected
std::vector< const char * > Expected
Definition: PrintASTTests.cpp:27
CI
std::unique_ptr< CompilerInvocation > CI
Definition: TUScheduler.cpp:450
TestTU.h
Filename
std::string Filename
Filename as a string.
Definition: IncludeOrderCheck.cpp:39
clang::clangd::IncludeStructure::HeaderID
HeaderID
Definition: Headers.h:119
clang::clangd::TEST_F
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
Definition: BackgroundIndexTests.cpp:94
SearchDirArg
std::string SearchDirArg
Definition: HeadersTests.cpp:137
Action
llvm::unique_function< void()> Action
Definition: TUScheduler.cpp:596
clang::clangd::IncludeStructure::MainFileIncludes
std::vector< Inclusion > MainFileIncludes
Definition: Headers.h:142
HeaderContents
std::vector< HeaderEntry > HeaderContents
Definition: Modularize.cpp:482
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
TestFS.h
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
clang::clangd::collectIncludeStructureCallback
std::unique_ptr< PPCallbacks > collectIncludeStructureCallback(const SourceManager &SM, IncludeStructure *Out)
Returns a PPCallback that visits all inclusions in the main file.
Definition: Headers.cpp:153
clang::clangd::TestTU::withCode
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:37
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:84
Entry
Definition: Modularize.cpp:428
Compiler.h
clang::clangd::PathRef
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:29
clang::clangd::MATCHER_P
MATCHER_P(Named, N, "")
Definition: BackgroundIndexTests.cpp:31
FS
MockFS FS
Definition: HeadersTests.cpp:133
Subdir
std::string Subdir
Definition: HeadersTests.cpp:136
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::IncludeStructure::includeDepth
llvm::DenseMap< HeaderID, unsigned > includeDepth(HeaderID Root) const
Definition: Headers.cpp:181
CDB
MockCompilationDatabase CDB
Definition: HeadersTests.cpp:134
clang::clangd::MockFS::Files
llvm::StringMap< std::string > Files
Definition: TestFS.h:47
MainFile
std::string MainFile
Definition: HeadersTests.cpp:135
clang::clangd::MockCompilationDatabase::ExtraClangFlags
std::vector< std::string > ExtraClangFlags
Definition: TestFS.h:70
IgnoreDiags
IgnoringDiagConsumer IgnoreDiags
Definition: HeadersTests.cpp:138
Clang
std::unique_ptr< CompilerInstance > Clang
Definition: HeadersTests.cpp:139
clang::clangd::IncludeStructure::IncludeChildren
llvm::DenseMap< HeaderID, SmallVector< HeaderID > > IncludeChildren
Definition: Headers.h:140