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