clang-tools 10.0.0

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
clang::clangd::MATCHER_P
MATCHER_P(Named, N, "")
Definition: BackgroundIndexTests.cpp:26
VFS
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS
Definition: CodeComplete.cpp:1023
Subdir
std::string Subdir
Definition: HeadersTests.cpp:122
clang::clangd::PathRef
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
FS
MockFSProvider FS
Definition: HeadersTests.cpp:119
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:72
clang::clangd::TextDocumentSyncKind::None
Documents should not be synced at all.
TestFS.h
TestTU.h
Headers.h
MainFile
std::string MainFile
Definition: HeadersTests.cpp:121
clang::clangd::TEST_F
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
Definition: BackgroundIndexTests.cpp:88
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:647
IgnoreDiags
IgnoringDiagConsumer IgnoreDiags
Definition: HeadersTests.cpp:124
clang::clangd::testPath
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
Compiler.h
SearchDirArg
std::string SearchDirArg
Definition: HeadersTests.cpp:123
Action
llvm::unique_function< void()> Action
Definition: TUScheduler.cpp:234
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:20
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::clangd::TestTU::withCode
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:33
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:113
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
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:44
clang::clangd::CompletionItemKind::File
CDB
MockCompilationDatabase CDB
Definition: HeadersTests.cpp:120