clang-tools  11.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/FormatVariadic.h"
21 #include "llvm/Support/Path.h"
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
24 
25 namespace clang {
26 namespace clangd {
27 namespace {
28 
29 using ::testing::AllOf;
30 using ::testing::Contains;
31 using ::testing::ElementsAre;
32 using ::testing::Not;
33 using ::testing::UnorderedElementsAre;
34 
35 class HeadersTest : public ::testing::Test {
36 public:
37  HeadersTest() {
38  CDB.ExtraClangFlags = {SearchDirArg.c_str()};
39  FS.Files[MainFile] = "";
40  // Make sure directory sub/ exists.
41  FS.Files[testPath("sub/EMPTY")] = "";
42  }
43 
44 private:
45  std::unique_ptr<CompilerInstance> setupClang() {
46  auto Cmd = CDB.getCompileCommand(MainFile);
47  assert(static_cast<bool>(Cmd));
48 
49  ParseInputs PI;
50  PI.CompileCommand = *Cmd;
51  PI.TFS = &FS;
53  EXPECT_TRUE(static_cast<bool>(CI));
54  // The diagnostic options must be set before creating a CompilerInstance.
55  CI->getDiagnosticOpts().IgnoreWarnings = true;
56  auto VFS = PI.TFS->view(Cmd->Directory);
57  auto Clang = prepareCompilerInstance(
58  std::move(CI), /*Preamble=*/nullptr,
59  llvm::MemoryBuffer::getMemBuffer(FS.Files[MainFile], MainFile),
60  std::move(VFS), IgnoreDiags);
61 
62  EXPECT_FALSE(Clang->getFrontendOpts().Inputs.empty());
63  return Clang;
64  }
65 
66 protected:
67  IncludeStructure collectIncludes() {
68  auto Clang = setupClang();
69  PreprocessOnlyAction Action;
70  EXPECT_TRUE(
71  Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
72  IncludeStructure Includes;
73  Clang->getPreprocessor().addPPCallbacks(
74  collectIncludeStructureCallback(Clang->getSourceManager(), &Includes));
75  EXPECT_FALSE(Action.Execute());
76  Action.EndSourceFile();
77  return Includes;
78  }
79 
80  // Calculates the include path, or returns "" on error or header should not be
81  // inserted.
82  std::string calculate(PathRef Original, PathRef Preferred = "",
83  const std::vector<Inclusion> &Inclusions = {}) {
84  auto Clang = setupClang();
85  PreprocessOnlyAction Action;
86  EXPECT_TRUE(
87  Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
88 
89  if (Preferred.empty())
90  Preferred = Original;
91  auto ToHeaderFile = [](llvm::StringRef Header) {
92  return HeaderFile{std::string(Header),
93  /*Verbatim=*/!llvm::sys::path::is_absolute(Header)};
94  };
95 
96  IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(),
97  CDB.getCompileCommand(MainFile)->Directory,
98  &Clang->getPreprocessor().getHeaderSearchInfo());
99  for (const auto &Inc : Inclusions)
100  Inserter.addExisting(Inc);
101  auto Inserted = ToHeaderFile(Preferred);
102  if (!Inserter.shouldInsertInclude(Original, Inserted))
103  return "";
104  auto Path = Inserter.calculateIncludePath(Inserted, MainFile);
105  Action.EndSourceFile();
106  return Path.getValueOr("");
107  }
108 
109  llvm::Optional<TextEdit> insert(llvm::StringRef VerbatimHeader) {
110  auto Clang = setupClang();
111  PreprocessOnlyAction Action;
112  EXPECT_TRUE(
113  Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
114 
115  IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(),
116  CDB.getCompileCommand(MainFile)->Directory,
117  &Clang->getPreprocessor().getHeaderSearchInfo());
118  auto Edit = Inserter.insert(VerbatimHeader);
119  Action.EndSourceFile();
120  return Edit;
121  }
122 
123  MockFS FS;
124  MockCompilationDatabase CDB;
125  std::string MainFile = testPath("main.cpp");
126  std::string Subdir = testPath("sub");
127  std::string SearchDirArg = (llvm::Twine("-I") + Subdir).str();
128  IgnoringDiagConsumer IgnoreDiags;
129 };
130 
131 MATCHER_P(Written, Name, "") { return arg.Written == Name; }
132 MATCHER_P(Resolved, Name, "") { return arg.Resolved == Name; }
133 MATCHER_P(IncludeLine, N, "") { return arg.HashLine == N; }
134 MATCHER_P(Directive, D, "") { return arg.Directive == D; }
135 
136 MATCHER_P2(Distance, File, D, "") {
137  if (arg.getKey() != File)
138  *result_listener << "file =" << arg.getKey().str();
139  if (arg.getValue() != D)
140  *result_listener << "distance =" << arg.getValue();
141  return arg.getKey() == File && arg.getValue() == D;
142 }
143 
144 TEST_F(HeadersTest, CollectRewrittenAndResolved) {
145  FS.Files[MainFile] = R"cpp(
146 #include "sub/bar.h" // not shortest
147 )cpp";
148  std::string BarHeader = testPath("sub/bar.h");
149  FS.Files[BarHeader] = "";
150 
151  EXPECT_THAT(collectIncludes().MainFileIncludes,
152  UnorderedElementsAre(
153  AllOf(Written("\"sub/bar.h\""), Resolved(BarHeader))));
154  EXPECT_THAT(collectIncludes().includeDepth(MainFile),
155  UnorderedElementsAre(Distance(MainFile, 0u),
156  Distance(testPath("sub/bar.h"), 1u)));
157 }
158 
159 TEST_F(HeadersTest, OnlyCollectInclusionsInMain) {
160  std::string BazHeader = testPath("sub/baz.h");
161  FS.Files[BazHeader] = "";
162  std::string BarHeader = testPath("sub/bar.h");
163  FS.Files[BarHeader] = R"cpp(
164 #include "baz.h"
165 )cpp";
166  FS.Files[MainFile] = R"cpp(
167 #include "bar.h"
168 )cpp";
169  EXPECT_THAT(
170  collectIncludes().MainFileIncludes,
171  UnorderedElementsAre(AllOf(Written("\"bar.h\""), Resolved(BarHeader))));
172  EXPECT_THAT(collectIncludes().includeDepth(MainFile),
173  UnorderedElementsAre(Distance(MainFile, 0u),
174  Distance(testPath("sub/bar.h"), 1u),
175  Distance(testPath("sub/baz.h"), 2u)));
176  // includeDepth() also works for non-main files.
177  EXPECT_THAT(collectIncludes().includeDepth(testPath("sub/bar.h")),
178  UnorderedElementsAre(Distance(testPath("sub/bar.h"), 0u),
179  Distance(testPath("sub/baz.h"), 1u)));
180 }
181 
182 TEST_F(HeadersTest, PreambleIncludesPresentOnce) {
183  // We use TestTU here, to ensure we use the preamble replay logic.
184  // We're testing that the logic doesn't crash, and doesn't result in duplicate
185  // includes. (We'd test more directly, but it's pretty well encapsulated!)
186  auto TU = TestTU::withCode(R"cpp(
187  #include "a.h"
188 
189  #include "a.h"
190  void foo();
191  #include "a.h"
192  )cpp");
193  TU.HeaderFilename = "a.h"; // suppress "not found".
194  EXPECT_THAT(TU.build().getIncludeStructure().MainFileIncludes,
195  ElementsAre(IncludeLine(1), IncludeLine(3), IncludeLine(5)));
196 }
197 
198 TEST_F(HeadersTest, UnResolvedInclusion) {
199  FS.Files[MainFile] = R"cpp(
200 #include "foo.h"
201 )cpp";
202 
203  EXPECT_THAT(collectIncludes().MainFileIncludes,
204  UnorderedElementsAre(AllOf(Written("\"foo.h\""), Resolved(""))));
205  EXPECT_THAT(collectIncludes().includeDepth(MainFile),
206  UnorderedElementsAre(Distance(MainFile, 0u)));
207 }
208 
209 TEST_F(HeadersTest, IncludeDirective) {
210  FS.Files[MainFile] = R"cpp(
211 #include "foo.h"
212 #import "foo.h"
213 #include_next "foo.h"
214 )cpp";
215 
216  // ms-compatibility changes meaning of #import, make sure it is turned off.
217  CDB.ExtraClangFlags.push_back("-fno-ms-compatibility");
218  EXPECT_THAT(collectIncludes().MainFileIncludes,
219  UnorderedElementsAre(Directive(tok::pp_include),
220  Directive(tok::pp_import),
221  Directive(tok::pp_include_next)));
222 }
223 
224 TEST_F(HeadersTest, InsertInclude) {
225  std::string Path = testPath("sub/bar.h");
226  FS.Files[Path] = "";
227  EXPECT_EQ(calculate(Path), "\"bar.h\"");
228 }
229 
230 TEST_F(HeadersTest, DoNotInsertIfInSameFile) {
231  MainFile = testPath("main.h");
232  EXPECT_EQ(calculate(MainFile), "");
233 }
234 
235 TEST_F(HeadersTest, DoNotInsertOffIncludePath) {
236  MainFile = testPath("sub/main.cpp");
237  EXPECT_EQ(calculate(testPath("sub2/main.cpp")), "");
238 }
239 
240 TEST_F(HeadersTest, ShortenIncludesInSearchPath) {
241  std::string BarHeader = testPath("sub/bar.h");
242  EXPECT_EQ(calculate(BarHeader), "\"bar.h\"");
243 
244  SearchDirArg = (llvm::Twine("-I") + Subdir + "/..").str();
245  CDB.ExtraClangFlags = {SearchDirArg.c_str()};
246  BarHeader = testPath("sub/bar.h");
247  EXPECT_EQ(calculate(BarHeader), "\"sub/bar.h\"");
248 }
249 
250 TEST_F(HeadersTest, ShortenedIncludeNotInSearchPath) {
251  std::string BarHeader =
252  llvm::sys::path::convert_to_slash(testPath("sub-2/bar.h"));
253  EXPECT_EQ(calculate(BarHeader, ""), "\"sub-2/bar.h\"");
254 }
255 
256 TEST_F(HeadersTest, PreferredHeader) {
257  std::string BarHeader = testPath("sub/bar.h");
258  EXPECT_EQ(calculate(BarHeader, "<bar>"), "<bar>");
259 
260  std::string BazHeader = testPath("sub/baz.h");
261  EXPECT_EQ(calculate(BarHeader, BazHeader), "\"baz.h\"");
262 }
263 
264 TEST_F(HeadersTest, DontInsertDuplicatePreferred) {
265  Inclusion Inc;
266  Inc.Written = "\"bar.h\"";
267  Inc.Resolved = "";
268  EXPECT_EQ(calculate(testPath("sub/bar.h"), "\"bar.h\"", {Inc}), "");
269  EXPECT_EQ(calculate("\"x.h\"", "\"bar.h\"", {Inc}), "");
270 }
271 
272 TEST_F(HeadersTest, DontInsertDuplicateResolved) {
273  Inclusion Inc;
274  Inc.Written = "fake-bar.h";
275  Inc.Resolved = testPath("sub/bar.h");
276  EXPECT_EQ(calculate(Inc.Resolved, "", {Inc}), "");
277  // Do not insert preferred.
278  EXPECT_EQ(calculate(Inc.Resolved, "\"BAR.h\"", {Inc}), "");
279 }
280 
281 TEST_F(HeadersTest, PreferInserted) {
282  auto Edit = insert("<y>");
283  EXPECT_TRUE(Edit.hasValue());
284  EXPECT_TRUE(StringRef(Edit->newText).contains("<y>"));
285 }
286 
287 TEST(Headers, NoHeaderSearchInfo) {
288  std::string MainFile = testPath("main.cpp");
289  IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(),
290  /*BuildDir=*/"", /*HeaderSearchInfo=*/nullptr);
291 
292  auto HeaderPath = testPath("sub/bar.h");
293  auto Inserting = HeaderFile{HeaderPath, /*Verbatim=*/false};
294  auto Verbatim = HeaderFile{"<x>", /*Verbatim=*/true};
295 
296  EXPECT_EQ(Inserter.calculateIncludePath(Inserting, MainFile),
297  std::string("\"sub/bar.h\""));
298  EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Inserting), false);
299 
300  EXPECT_EQ(Inserter.calculateIncludePath(Verbatim, MainFile),
301  std::string("<x>"));
302  EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Verbatim), true);
303 
304  EXPECT_EQ(Inserter.calculateIncludePath(Inserting, "sub2/main2.cpp"),
305  llvm::None);
306 }
307 
308 TEST_F(HeadersTest, PresumedLocations) {
309  std::string HeaderFile = "__preamble_patch__.h";
310 
311  // Line map inclusion back to main file.
312  std::string HeaderContents =
313  llvm::formatv("#line 0 \"{0}\"", llvm::sys::path::filename(MainFile));
314  HeaderContents += R"cpp(
315 #line 3
316 #include <a.h>)cpp";
317  FS.Files[HeaderFile] = HeaderContents;
318 
319  // Including through non-builtin file has no effects.
320  FS.Files[MainFile] = "#include \"__preamble_patch__.h\"\n\n";
321  EXPECT_THAT(collectIncludes().MainFileIncludes,
322  Not(Contains(Written("<a.h>"))));
323 
324  // Now include through built-in file.
325  CDB.ExtraClangFlags = {"-include", testPath(HeaderFile)};
326  EXPECT_THAT(collectIncludes().MainFileIncludes,
327  Contains(AllOf(IncludeLine(2), Written("<a.h>"))));
328 }
329 } // namespace
330 } // namespace clangd
331 } // namespace clang
MATCHER_P(Named, N, "")
std::string Subdir
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
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:91
Documents should not be synced at all.
std::unique_ptr< CompilerInvocation > CI
std::string MainFile
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
TEST(BackgroundQueueTest, Priority)
IgnoringDiagConsumer IgnoreDiags
std::string SearchDirArg
llvm::unique_function< void()> Action
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
std::string Path
A typedef to represent a file path.
Definition: Path.h:20
static constexpr llvm::StringLiteral Name
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:35
std::unique_ptr< PPCallbacks > collectIncludeStructureCallback(const SourceManager &SM, IncludeStructure *Out)
Returns a PPCallback that visits all inclusions in the main file.
Definition: Headers.cpp:152
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
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:45
std::vector< HeaderEntry > HeaderContents
Definition: Modularize.cpp:483
MockFS FS
MockCompilationDatabase CDB