[AMDGPU][AsmParser][NFC] Translate parsed MIMG instructions to MCInsts automatically.
[llvm-project.git] / clang-tools-extra / clangd / unittests / HeadersTests.cpp
blob8c0eae6bb3cc8bc7af5b7c3b797a9fe66601c661
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 //===----------------------------------------------------------------------===//
9 #include "Headers.h"
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 "clang/Tooling/Inclusions/HeaderIncludes.h"
20 #include "llvm/ADT/StringRef.h"
21 #include "llvm/Support/Error.h"
22 #include "llvm/Support/FormatVariadic.h"
23 #include "llvm/Support/Path.h"
24 #include "llvm/Testing/Support/Error.h"
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 #include <optional>
29 namespace clang {
30 namespace clangd {
31 namespace {
33 using ::testing::AllOf;
34 using ::testing::Contains;
35 using ::testing::ElementsAre;
36 using ::testing::Eq;
37 using ::testing::IsEmpty;
38 using ::testing::Not;
39 using ::testing::UnorderedElementsAre;
41 class HeadersTest : public ::testing::Test {
42 public:
43 HeadersTest() {
44 CDB.ExtraClangFlags = {SearchDirArg.c_str()};
45 FS.Files[MainFile] = "";
46 // Make sure directory sub/ exists.
47 FS.Files[testPath("sub/EMPTY")] = "";
50 private:
51 std::unique_ptr<CompilerInstance> setupClang() {
52 auto Cmd = CDB.getCompileCommand(MainFile);
53 assert(static_cast<bool>(Cmd));
55 ParseInputs PI;
56 PI.CompileCommand = *Cmd;
57 PI.TFS = &FS;
58 auto CI = buildCompilerInvocation(PI, IgnoreDiags);
59 EXPECT_TRUE(static_cast<bool>(CI));
60 // The diagnostic options must be set before creating a CompilerInstance.
61 CI->getDiagnosticOpts().IgnoreWarnings = true;
62 auto VFS = PI.TFS->view(Cmd->Directory);
63 auto Clang = prepareCompilerInstance(
64 std::move(CI), /*Preamble=*/nullptr,
65 llvm::MemoryBuffer::getMemBuffer(FS.Files[MainFile], MainFile),
66 std::move(VFS), IgnoreDiags);
68 EXPECT_FALSE(Clang->getFrontendOpts().Inputs.empty());
69 return Clang;
72 protected:
73 IncludeStructure::HeaderID getID(StringRef Filename,
74 IncludeStructure &Includes) {
75 auto &SM = Clang->getSourceManager();
76 auto Entry = SM.getFileManager().getFileRef(Filename);
77 EXPECT_THAT_EXPECTED(Entry, llvm::Succeeded());
78 return Includes.getOrCreateID(*Entry);
81 IncludeStructure collectIncludes() {
82 Clang = setupClang();
83 PreprocessOnlyAction Action;
84 EXPECT_TRUE(
85 Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
86 IncludeStructure Includes;
87 Includes.collect(*Clang);
88 EXPECT_FALSE(Action.Execute());
89 Action.EndSourceFile();
90 return Includes;
93 // Calculates the include path, or returns "" on error or header should not be
94 // inserted.
95 std::string calculate(PathRef Original, PathRef Preferred = "",
96 const std::vector<Inclusion> &Inclusions = {}) {
97 Clang = setupClang();
98 PreprocessOnlyAction Action;
99 EXPECT_TRUE(
100 Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
102 if (Preferred.empty())
103 Preferred = Original;
104 auto ToHeaderFile = [](llvm::StringRef Header) {
105 return HeaderFile{std::string(Header),
106 /*Verbatim=*/!llvm::sys::path::is_absolute(Header)};
109 IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(),
110 CDB.getCompileCommand(MainFile)->Directory,
111 &Clang->getPreprocessor().getHeaderSearchInfo());
112 for (const auto &Inc : Inclusions)
113 Inserter.addExisting(Inc);
114 auto Inserted = ToHeaderFile(Preferred);
115 if (!Inserter.shouldInsertInclude(Original, Inserted))
116 return "";
117 auto Path = Inserter.calculateIncludePath(Inserted, MainFile);
118 Action.EndSourceFile();
119 return Path.value_or("");
122 std::optional<TextEdit> insert(llvm::StringRef VerbatimHeader,
123 tooling::IncludeDirective Directive) {
124 Clang = setupClang();
125 PreprocessOnlyAction Action;
126 EXPECT_TRUE(
127 Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
129 IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(),
130 CDB.getCompileCommand(MainFile)->Directory,
131 &Clang->getPreprocessor().getHeaderSearchInfo());
132 auto Edit = Inserter.insert(VerbatimHeader, Directive);
133 Action.EndSourceFile();
134 return Edit;
137 MockFS FS;
138 MockCompilationDatabase CDB;
139 std::string MainFile = testPath("main.cpp");
140 std::string Subdir = testPath("sub");
141 std::string SearchDirArg = (llvm::Twine("-I") + Subdir).str();
142 IgnoringDiagConsumer IgnoreDiags;
143 std::unique_ptr<CompilerInstance> Clang;
146 MATCHER_P(written, Name, "") { return arg.Written == Name; }
147 MATCHER_P(resolved, Name, "") { return arg.Resolved == Name; }
148 MATCHER_P(includeLine, N, "") { return arg.HashLine == N; }
149 MATCHER_P(directive, D, "") { return arg.Directive == D; }
151 MATCHER_P2(Distance, File, D, "") {
152 if (arg.getFirst() != File)
153 *result_listener << "file =" << static_cast<unsigned>(arg.getFirst());
154 if (arg.getSecond() != D)
155 *result_listener << "distance =" << arg.getSecond();
156 return arg.getFirst() == File && arg.getSecond() == D;
159 TEST_F(HeadersTest, CollectRewrittenAndResolved) {
160 FS.Files[MainFile] = R"cpp(
161 #include "sub/bar.h" // not shortest
162 )cpp";
163 std::string BarHeader = testPath("sub/bar.h");
164 FS.Files[BarHeader] = "";
166 auto Includes = collectIncludes();
167 EXPECT_THAT(Includes.MainFileIncludes,
168 UnorderedElementsAre(
169 AllOf(written("\"sub/bar.h\""), resolved(BarHeader))));
170 EXPECT_THAT(Includes.includeDepth(getID(MainFile, Includes)),
171 UnorderedElementsAre(Distance(getID(MainFile, Includes), 0u),
172 Distance(getID(BarHeader, Includes), 1u)));
175 TEST_F(HeadersTest, OnlyCollectInclusionsInMain) {
176 std::string BazHeader = testPath("sub/baz.h");
177 FS.Files[BazHeader] = "";
178 std::string BarHeader = testPath("sub/bar.h");
179 FS.Files[BarHeader] = R"cpp(
180 #include "baz.h"
181 )cpp";
182 FS.Files[MainFile] = R"cpp(
183 #include "bar.h"
184 )cpp";
185 auto Includes = collectIncludes();
186 EXPECT_THAT(
187 Includes.MainFileIncludes,
188 UnorderedElementsAre(AllOf(written("\"bar.h\""), resolved(BarHeader))));
189 EXPECT_THAT(Includes.includeDepth(getID(MainFile, Includes)),
190 UnorderedElementsAre(Distance(getID(MainFile, Includes), 0u),
191 Distance(getID(BarHeader, Includes), 1u),
192 Distance(getID(BazHeader, Includes), 2u)));
193 // includeDepth() also works for non-main files.
194 EXPECT_THAT(Includes.includeDepth(getID(BarHeader, Includes)),
195 UnorderedElementsAre(Distance(getID(BarHeader, Includes), 0u),
196 Distance(getID(BazHeader, Includes), 1u)));
199 TEST_F(HeadersTest, CacheBySpellingIsBuiltForMainInclusions) {
200 std::string FooHeader = testPath("foo.h");
201 FS.Files[FooHeader] = R"cpp(
202 void foo();
203 )cpp";
204 std::string BarHeader = testPath("bar.h");
205 FS.Files[BarHeader] = R"cpp(
206 void bar();
207 )cpp";
208 std::string BazHeader = testPath("baz.h");
209 FS.Files[BazHeader] = R"cpp(
210 void baz();
211 )cpp";
212 FS.Files[MainFile] = R"cpp(
213 #include "foo.h"
214 #include "bar.h"
215 #include "baz.h"
216 )cpp";
217 auto Includes = collectIncludes();
218 EXPECT_THAT(Includes.MainFileIncludes,
219 UnorderedElementsAre(written("\"foo.h\""), written("\"bar.h\""),
220 written("\"baz.h\"")));
221 EXPECT_THAT(Includes.mainFileIncludesWithSpelling("\"foo.h\""),
222 UnorderedElementsAre(&Includes.MainFileIncludes[0]));
223 EXPECT_THAT(Includes.mainFileIncludesWithSpelling("\"bar.h\""),
224 UnorderedElementsAre(&Includes.MainFileIncludes[1]));
225 EXPECT_THAT(Includes.mainFileIncludesWithSpelling("\"baz.h\""),
226 UnorderedElementsAre(&Includes.MainFileIncludes[2]));
229 TEST_F(HeadersTest, PreambleIncludesPresentOnce) {
230 // We use TestTU here, to ensure we use the preamble replay logic.
231 // We're testing that the logic doesn't crash, and doesn't result in duplicate
232 // includes. (We'd test more directly, but it's pretty well encapsulated!)
233 auto TU = TestTU::withCode(R"cpp(
234 #include "a.h"
236 #include "a.h"
237 void foo();
238 #include "a.h"
239 )cpp");
240 TU.HeaderFilename = "a.h"; // suppress "not found".
241 EXPECT_THAT(TU.build().getIncludeStructure().MainFileIncludes,
242 ElementsAre(includeLine(1), includeLine(3), includeLine(5)));
245 TEST_F(HeadersTest, UnResolvedInclusion) {
246 FS.Files[MainFile] = R"cpp(
247 #include "foo.h"
248 )cpp";
250 EXPECT_THAT(collectIncludes().MainFileIncludes,
251 UnorderedElementsAre(AllOf(written("\"foo.h\""), resolved(""))));
252 EXPECT_THAT(collectIncludes().IncludeChildren, IsEmpty());
255 TEST_F(HeadersTest, IncludedFilesGraph) {
256 FS.Files[MainFile] = R"cpp(
257 #include "bar.h"
258 #include "foo.h"
259 )cpp";
260 std::string BarHeader = testPath("bar.h");
261 FS.Files[BarHeader] = "";
262 std::string FooHeader = testPath("foo.h");
263 FS.Files[FooHeader] = R"cpp(
264 #include "bar.h"
265 #include "baz.h"
266 )cpp";
267 std::string BazHeader = testPath("baz.h");
268 FS.Files[BazHeader] = "";
270 auto Includes = collectIncludes();
271 llvm::DenseMap<IncludeStructure::HeaderID,
272 SmallVector<IncludeStructure::HeaderID>>
273 Expected = {{getID(MainFile, Includes),
274 {getID(BarHeader, Includes), getID(FooHeader, Includes)}},
275 {getID(FooHeader, Includes),
276 {getID(BarHeader, Includes), getID(BazHeader, Includes)}}};
277 EXPECT_EQ(Includes.IncludeChildren, Expected);
280 TEST_F(HeadersTest, IncludeDirective) {
281 FS.Files[MainFile] = R"cpp(
282 #include "foo.h"
283 #import "foo.h"
284 #include_next "foo.h"
285 )cpp";
287 // ms-compatibility changes meaning of #import, make sure it is turned off.
288 CDB.ExtraClangFlags.push_back("-fno-ms-compatibility");
289 EXPECT_THAT(collectIncludes().MainFileIncludes,
290 UnorderedElementsAre(directive(tok::pp_include),
291 directive(tok::pp_import),
292 directive(tok::pp_include_next)));
295 TEST_F(HeadersTest, InsertInclude) {
296 std::string Path = testPath("sub/bar.h");
297 FS.Files[Path] = "";
298 EXPECT_EQ(calculate(Path), "\"bar.h\"");
301 TEST_F(HeadersTest, DoNotInsertIfInSameFile) {
302 MainFile = testPath("main.h");
303 EXPECT_EQ(calculate(MainFile), "");
306 TEST_F(HeadersTest, DoNotInsertOffIncludePath) {
307 MainFile = testPath("sub/main.cpp");
308 EXPECT_EQ(calculate(testPath("sub2/main.cpp")), "");
311 TEST_F(HeadersTest, ShortenIncludesInSearchPath) {
312 std::string BarHeader = testPath("sub/bar.h");
313 EXPECT_EQ(calculate(BarHeader), "\"bar.h\"");
315 SearchDirArg = (llvm::Twine("-I") + Subdir + "/..").str();
316 CDB.ExtraClangFlags = {SearchDirArg.c_str()};
317 BarHeader = testPath("sub/bar.h");
318 EXPECT_EQ(calculate(BarHeader), "\"sub/bar.h\"");
321 TEST_F(HeadersTest, ShortenedIncludeNotInSearchPath) {
322 std::string BarHeader =
323 llvm::sys::path::convert_to_slash(testPath("sub-2/bar.h"));
324 EXPECT_EQ(calculate(BarHeader, ""), "\"sub-2/bar.h\"");
327 TEST_F(HeadersTest, PreferredHeader) {
328 std::string BarHeader = testPath("sub/bar.h");
329 EXPECT_EQ(calculate(BarHeader, "<bar>"), "<bar>");
331 std::string BazHeader = testPath("sub/baz.h");
332 EXPECT_EQ(calculate(BarHeader, BazHeader), "\"baz.h\"");
335 TEST_F(HeadersTest, DontInsertDuplicatePreferred) {
336 Inclusion Inc;
337 Inc.Written = "\"bar.h\"";
338 Inc.Resolved = "";
339 EXPECT_EQ(calculate(testPath("sub/bar.h"), "\"bar.h\"", {Inc}), "");
340 EXPECT_EQ(calculate("\"x.h\"", "\"bar.h\"", {Inc}), "");
343 TEST_F(HeadersTest, DontInsertDuplicateResolved) {
344 Inclusion Inc;
345 Inc.Written = "fake-bar.h";
346 Inc.Resolved = testPath("sub/bar.h");
347 EXPECT_EQ(calculate(Inc.Resolved, "", {Inc}), "");
348 // Do not insert preferred.
349 EXPECT_EQ(calculate(Inc.Resolved, "\"BAR.h\"", {Inc}), "");
352 TEST_F(HeadersTest, PreferInserted) {
353 auto Edit = insert("<y>", tooling::IncludeDirective::Include);
354 ASSERT_TRUE(Edit);
355 EXPECT_EQ(Edit->newText, "#include <y>\n");
357 Edit = insert("\"header.h\"", tooling::IncludeDirective::Import);
358 ASSERT_TRUE(Edit);
359 EXPECT_EQ(Edit->newText, "#import \"header.h\"\n");
362 TEST(Headers, NoHeaderSearchInfo) {
363 std::string MainFile = testPath("main.cpp");
364 IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(),
365 /*BuildDir=*/"", /*HeaderSearchInfo=*/nullptr);
367 auto HeaderPath = testPath("sub/bar.h");
368 auto Inserting = HeaderFile{HeaderPath, /*Verbatim=*/false};
369 auto Verbatim = HeaderFile{"<x>", /*Verbatim=*/true};
371 EXPECT_EQ(Inserter.calculateIncludePath(Inserting, MainFile),
372 std::string("\"sub/bar.h\""));
373 EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Inserting), false);
375 EXPECT_EQ(Inserter.calculateIncludePath(Verbatim, MainFile),
376 std::string("<x>"));
377 EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Verbatim), true);
379 EXPECT_EQ(Inserter.calculateIncludePath(Inserting, "sub2/main2.cpp"),
380 std::nullopt);
383 TEST_F(HeadersTest, PresumedLocations) {
384 std::string HeaderFile = "__preamble_patch__.h";
386 // Line map inclusion back to main file.
387 std::string HeaderContents =
388 llvm::formatv("#line 0 \"{0}\"", llvm::sys::path::filename(MainFile));
389 HeaderContents += R"cpp(
390 #line 3
391 #include <a.h>)cpp";
392 FS.Files[HeaderFile] = HeaderContents;
394 // Including through non-builtin file has no effects.
395 FS.Files[MainFile] = "#include \"__preamble_patch__.h\"\n\n";
396 EXPECT_THAT(collectIncludes().MainFileIncludes,
397 Not(Contains(written("<a.h>"))));
399 // Now include through built-in file.
400 CDB.ExtraClangFlags = {"-include", testPath(HeaderFile)};
401 EXPECT_THAT(collectIncludes().MainFileIncludes,
402 Contains(AllOf(includeLine(2), written("<a.h>"))));
405 } // namespace
406 } // namespace clangd
407 } // namespace clang