1 | //===- unittests/Serialization/ForceCheckFileInputTest.cpp - CI tests -----===// |
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 "clang/ASTMatchers/ASTMatchFinder.h" |
10 | #include "clang/ASTMatchers/ASTMatchers.h" |
11 | #include "clang/Basic/FileManager.h" |
12 | #include "clang/Frontend/CompilerInstance.h" |
13 | #include "clang/Frontend/CompilerInvocation.h" |
14 | #include "clang/Frontend/FrontendActions.h" |
15 | #include "clang/Frontend/Utils.h" |
16 | #include "clang/Lex/HeaderSearch.h" |
17 | #include "clang/Lex/PreprocessorOptions.h" |
18 | #include "clang/Serialization/ASTReader.h" |
19 | #include "clang/Tooling/Tooling.h" |
20 | #include "llvm/ADT/SmallString.h" |
21 | #include "llvm/Support/FileSystem.h" |
22 | #include "llvm/Support/raw_ostream.h" |
23 | |
24 | #include "gtest/gtest.h" |
25 | |
26 | using namespace llvm; |
27 | using namespace clang; |
28 | |
29 | namespace { |
30 | |
31 | class ForceCheckFileInputTest : public ::testing::Test { |
32 | void SetUp() override { |
33 | EXPECT_FALSE(sys::fs::createUniqueDirectory("modules-test" , TestDir)); |
34 | } |
35 | |
36 | void TearDown() override { sys::fs::remove_directories(path: TestDir); } |
37 | |
38 | public: |
39 | SmallString<256> TestDir; |
40 | |
41 | void addFile(StringRef Path, StringRef Contents) { |
42 | EXPECT_FALSE(sys::path::is_absolute(Path)); |
43 | |
44 | SmallString<256> AbsPath(TestDir); |
45 | sys::path::append(path&: AbsPath, a: Path); |
46 | |
47 | EXPECT_FALSE( |
48 | sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath))); |
49 | |
50 | std::error_code EC; |
51 | llvm::raw_fd_ostream OS(AbsPath, EC); |
52 | EXPECT_FALSE(EC); |
53 | OS << Contents; |
54 | } |
55 | }; |
56 | |
57 | TEST_F(ForceCheckFileInputTest, ForceCheck) { |
58 | addFile(Path: "a.cppm" , Contents: R"cpp( |
59 | export module a; |
60 | export int aa = 43; |
61 | )cpp" ); |
62 | |
63 | std::string BMIPath = llvm::Twine(TestDir + "/a.pcm" ).str(); |
64 | |
65 | { |
66 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags = |
67 | CompilerInstance::createDiagnostics(Opts: new DiagnosticOptions()); |
68 | CreateInvocationOptions CIOpts; |
69 | CIOpts.Diags = Diags; |
70 | CIOpts.VFS = llvm::vfs::createPhysicalFileSystem(); |
71 | |
72 | const char *Args[] = {"clang++" , "-std=c++20" , |
73 | "--precompile" , "-working-directory" , |
74 | TestDir.c_str(), "a.cppm" }; |
75 | std::shared_ptr<CompilerInvocation> Invocation = |
76 | createInvocation(Args, Opts: CIOpts); |
77 | EXPECT_TRUE(Invocation); |
78 | Invocation->getFrontendOpts().DisableFree = false; |
79 | |
80 | auto Buf = CIOpts.VFS->getBufferForFile(Name: "a.cppm" ); |
81 | EXPECT_TRUE(Buf); |
82 | |
83 | Invocation->getPreprocessorOpts().addRemappedFile(From: "a.cppm" , To: Buf->get()); |
84 | |
85 | Buf->release(); |
86 | |
87 | CompilerInstance Instance; |
88 | Instance.setDiagnostics(Diags.get()); |
89 | Instance.setInvocation(Invocation); |
90 | |
91 | Instance.getFrontendOpts().OutputFile = BMIPath; |
92 | |
93 | if (auto VFSWithRemapping = createVFSFromCompilerInvocation( |
94 | CI: Instance.getInvocation(), Diags&: Instance.getDiagnostics(), BaseFS: CIOpts.VFS)) |
95 | CIOpts.VFS = VFSWithRemapping; |
96 | Instance.createFileManager(VFS: CIOpts.VFS); |
97 | |
98 | Instance.getHeaderSearchOpts().ValidateASTInputFilesContent = true; |
99 | |
100 | GenerateReducedModuleInterfaceAction Action; |
101 | EXPECT_TRUE(Instance.ExecuteAction(Action)); |
102 | EXPECT_FALSE(Diags->hasErrorOccurred()); |
103 | } |
104 | |
105 | { |
106 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags = |
107 | CompilerInstance::createDiagnostics(Opts: new DiagnosticOptions()); |
108 | CreateInvocationOptions CIOpts; |
109 | CIOpts.Diags = Diags; |
110 | CIOpts.VFS = llvm::vfs::createPhysicalFileSystem(); |
111 | |
112 | std::string BMIPath = llvm::Twine(TestDir + "/a.pcm" ).str(); |
113 | const char *Args[] = { |
114 | "clang++" , "-std=c++20" , "--precompile" , "-working-directory" , |
115 | TestDir.c_str(), "a.cppm" , "-o" , BMIPath.c_str()}; |
116 | std::shared_ptr<CompilerInvocation> Invocation = |
117 | createInvocation(Args, Opts: CIOpts); |
118 | EXPECT_TRUE(Invocation); |
119 | Invocation->getFrontendOpts().DisableFree = false; |
120 | |
121 | CompilerInstance Clang; |
122 | |
123 | Clang.setInvocation(Invocation); |
124 | Clang.setDiagnostics(Diags.get()); |
125 | FileManager *FM = Clang.createFileManager(VFS: CIOpts.VFS); |
126 | Clang.createSourceManager(FileMgr&: *FM); |
127 | |
128 | EXPECT_TRUE(Clang.createTarget()); |
129 | Clang.createPreprocessor(TUKind: TU_Complete); |
130 | Clang.getHeaderSearchOpts().ForceCheckCXX20ModulesInputFiles = true; |
131 | Clang.getHeaderSearchOpts().ValidateASTInputFilesContent = true; |
132 | Clang.createASTReader(); |
133 | |
134 | addFile(Path: "a.cppm" , Contents: R"cpp( |
135 | export module a; |
136 | export int aa = 44; |
137 | )cpp" ); |
138 | |
139 | auto ReadResult = |
140 | Clang.getASTReader()->ReadAST(FileName: BMIPath, Type: serialization::MK_MainFile, |
141 | ImportLoc: SourceLocation(), ClientLoadCapabilities: ASTReader::ARR_None); |
142 | |
143 | // We shall be able to detect the content change here. |
144 | EXPECT_NE(ReadResult, ASTReader::Success); |
145 | } |
146 | } |
147 | |
148 | } // anonymous namespace |
149 | |