1 | //===-- ModulesTests.cpp ---------------------------------------*- 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 "TestFS.h" |
10 | #include "TestTU.h" |
11 | #include "llvm/ADT/StringRef.h" |
12 | #include "gmock/gmock.h" |
13 | #include "gtest/gtest.h" |
14 | |
15 | #include <memory> |
16 | #include <string> |
17 | |
18 | namespace clang { |
19 | namespace clangd { |
20 | namespace { |
21 | |
22 | TEST(Modules, TextualIncludeInPreamble) { |
23 | TestTU TU = TestTU::withCode(Code: R"cpp( |
24 | #include "Textual.h" |
25 | |
26 | void foo() {} |
27 | )cpp" ); |
28 | TU.ExtraArgs.push_back(x: "-fmodule-name=M" ); |
29 | TU.ExtraArgs.push_back(x: "-fmodule-map-file=" + testPath(File: "m.modulemap" )); |
30 | TU.AdditionalFiles["Textual.h" ] = "void foo();" ; |
31 | TU.AdditionalFiles["m.modulemap" ] = R"modulemap( |
32 | module M { |
33 | module Textual { |
34 | textual header "Textual.h" |
35 | } |
36 | } |
37 | )modulemap" ; |
38 | // Test that we do not crash. |
39 | TU.index(); |
40 | } |
41 | |
42 | // Verify that visibility of AST nodes belonging to modules, but loaded from |
43 | // preamble PCH, is restored. |
44 | TEST(Modules, PreambleBuildVisibility) { |
45 | TestTU TU = TestTU::withCode(Code: R"cpp( |
46 | #include "module.h" |
47 | |
48 | foo x; |
49 | )cpp" ); |
50 | TU.OverlayRealFileSystemForModules = true; |
51 | TU.ExtraArgs.push_back(x: "-fmodules" ); |
52 | TU.ExtraArgs.push_back(x: "-fmodules-strict-decluse" ); |
53 | TU.ExtraArgs.push_back(x: "-Xclang" ); |
54 | TU.ExtraArgs.push_back(x: "-fmodules-local-submodule-visibility" ); |
55 | TU.ExtraArgs.push_back(x: "-fmodule-map-file=" + testPath(File: "m.modulemap" )); |
56 | TU.AdditionalFiles["module.h" ] = R"cpp( |
57 | typedef int foo; |
58 | )cpp" ; |
59 | TU.AdditionalFiles["m.modulemap" ] = R"modulemap( |
60 | module M { |
61 | header "module.h" |
62 | } |
63 | )modulemap" ; |
64 | EXPECT_TRUE(TU.build().getDiagnostics().empty()); |
65 | } |
66 | |
67 | TEST(Modules, Diagnostic) { |
68 | // Produce a diagnostic while building an implicit module. Use |
69 | // -fmodules-strict-decluse, but any non-silenced diagnostic will do. |
70 | TestTU TU = TestTU::withCode(Code: R"cpp( |
71 | /*error-ok*/ |
72 | #include "modular.h" |
73 | |
74 | void bar() {} |
75 | )cpp" ); |
76 | TU.OverlayRealFileSystemForModules = true; |
77 | TU.ExtraArgs.push_back(x: "-fmodule-map-file=" + testPath(File: "m.modulemap" )); |
78 | TU.ExtraArgs.push_back(x: "-fmodules" ); |
79 | TU.ExtraArgs.push_back(x: "-fimplicit-modules" ); |
80 | TU.ExtraArgs.push_back(x: "-fmodules-strict-decluse" ); |
81 | TU.AdditionalFiles["modular.h" ] = R"cpp( |
82 | #include "non-modular.h" |
83 | )cpp" ; |
84 | TU.AdditionalFiles["non-modular.h" ] = "" ; |
85 | TU.AdditionalFiles["m.modulemap" ] = R"modulemap( |
86 | module M { |
87 | header "modular.h" |
88 | } |
89 | )modulemap" ; |
90 | |
91 | // Test that we do not crash. |
92 | TU.build(); |
93 | } |
94 | |
95 | // Unknown module formats are a fatal failure for clang. Ensure we don't crash. |
96 | TEST(Modules, UnknownFormat) { |
97 | TestTU TU = TestTU::withCode(Code: R"(#include "modular.h")" ); |
98 | TU.OverlayRealFileSystemForModules = true; |
99 | TU.ExtraArgs.push_back(x: "-Xclang" ); |
100 | TU.ExtraArgs.push_back(x: "-fmodule-format=obj" ); |
101 | TU.ExtraArgs.push_back(x: "-fmodule-map-file=" + testPath(File: "m.modulemap" )); |
102 | TU.ExtraArgs.push_back(x: "-fmodules" ); |
103 | TU.ExtraArgs.push_back(x: "-fimplicit-modules" ); |
104 | TU.AdditionalFiles["modular.h" ] = "" ; |
105 | TU.AdditionalFiles["m.modulemap" ] = R"modulemap( |
106 | module M { |
107 | header "modular.h" |
108 | })modulemap" ; |
109 | |
110 | // Test that we do not crash. |
111 | TU.build(); |
112 | } |
113 | |
114 | // Test that we can build and use a preamble for a module unit. |
115 | TEST(Modules, ModulePreamble) { |
116 | TestTU TU = TestTU::withCode(Code: R"cpp( |
117 | module; |
118 | #define PREAMBLE_MACRO 1 |
119 | export module foo; |
120 | #define MODULE_MACRO 2 |
121 | module :private; |
122 | #define PRIVATE_MACRO 3 |
123 | )cpp" ); |
124 | TU.ExtraArgs.push_back(x: "-std=c++20" ); |
125 | TU.ExtraArgs.push_back(x: "--precompile" ); |
126 | |
127 | auto AST = TU.build(); |
128 | auto &SM = AST.getSourceManager(); |
129 | auto GetMacroFile = [&](llvm::StringRef Name) -> FileID { |
130 | if (auto *MI = AST.getPreprocessor().getMacroInfo( |
131 | II: &AST.getASTContext().Idents.get(Name))) |
132 | return SM.getFileID(SpellingLoc: MI->getDefinitionLoc()); |
133 | return {}; |
134 | }; |
135 | |
136 | EXPECT_EQ(GetMacroFile("PREAMBLE_MACRO" ), SM.getPreambleFileID()); |
137 | EXPECT_EQ(GetMacroFile("MODULE_MACRO" ), SM.getMainFileID()); |
138 | EXPECT_EQ(GetMacroFile("PRIVATE_MACRO" ), SM.getMainFileID()); |
139 | } |
140 | |
141 | } // namespace |
142 | } // namespace clangd |
143 | } // namespace clang |
144 | |