1 | //===- unittest/Tooling/ToolingTest.cpp - Tooling unit 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/Tooling/Tooling.h" |
10 | #include "clang/AST/ASTConsumer.h" |
11 | #include "clang/AST/DeclCXX.h" |
12 | #include "clang/AST/DeclGroup.h" |
13 | #include "clang/Driver/Compilation.h" |
14 | #include "clang/Driver/Driver.h" |
15 | #include "clang/Frontend/ASTUnit.h" |
16 | #include "clang/Frontend/CompilerInstance.h" |
17 | #include "clang/Frontend/FrontendAction.h" |
18 | #include "clang/Frontend/FrontendActions.h" |
19 | #include "clang/Frontend/TextDiagnosticBuffer.h" |
20 | #include "clang/Testing/CommandLineArgs.h" |
21 | #include "clang/Tooling/ArgumentsAdjusters.h" |
22 | #include "clang/Tooling/CompilationDatabase.h" |
23 | #include "llvm/ADT/STLExtras.h" |
24 | #include "llvm/ADT/StringRef.h" |
25 | #include "llvm/Support/Path.h" |
26 | #include "llvm/Support/TargetSelect.h" |
27 | #include "llvm/TargetParser/Host.h" |
28 | #include "gtest/gtest.h" |
29 | #include <algorithm> |
30 | #include <string> |
31 | #include <vector> |
32 | |
33 | namespace clang { |
34 | namespace tooling { |
35 | |
36 | namespace { |
37 | /// Takes an ast consumer and returns it from CreateASTConsumer. This only |
38 | /// works with single translation unit compilations. |
39 | class TestAction : public clang::ASTFrontendAction { |
40 | public: |
41 | /// Takes ownership of TestConsumer. |
42 | explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer) |
43 | : TestConsumer(std::move(TestConsumer)) {} |
44 | |
45 | protected: |
46 | std::unique_ptr<clang::ASTConsumer> |
47 | CreateASTConsumer(clang::CompilerInstance &compiler, |
48 | StringRef dummy) override { |
49 | /// TestConsumer will be deleted by the framework calling us. |
50 | return std::move(TestConsumer); |
51 | } |
52 | |
53 | private: |
54 | std::unique_ptr<clang::ASTConsumer> TestConsumer; |
55 | }; |
56 | |
57 | class FindTopLevelDeclConsumer : public clang::ASTConsumer { |
58 | public: |
59 | explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl) |
60 | : FoundTopLevelDecl(FoundTopLevelDecl) {} |
61 | bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override { |
62 | *FoundTopLevelDecl = true; |
63 | return true; |
64 | } |
65 | private: |
66 | bool * const FoundTopLevelDecl; |
67 | }; |
68 | } // end namespace |
69 | |
70 | TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) { |
71 | bool FoundTopLevelDecl = false; |
72 | EXPECT_TRUE(runToolOnCode( |
73 | std::make_unique<TestAction>( |
74 | std::make_unique<FindTopLevelDeclConsumer>(&FoundTopLevelDecl)), |
75 | "" )); |
76 | EXPECT_FALSE(FoundTopLevelDecl); |
77 | } |
78 | |
79 | namespace { |
80 | class FindClassDeclXConsumer : public clang::ASTConsumer { |
81 | public: |
82 | FindClassDeclXConsumer(bool *FoundClassDeclX) |
83 | : FoundClassDeclX(FoundClassDeclX) {} |
84 | bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override { |
85 | if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>( |
86 | Val: *GroupRef.begin())) { |
87 | if (Record->getName() == "X" ) { |
88 | *FoundClassDeclX = true; |
89 | } |
90 | } |
91 | return true; |
92 | } |
93 | private: |
94 | bool *FoundClassDeclX; |
95 | }; |
96 | bool FindClassDeclX(ASTUnit *AST) { |
97 | for (std::vector<Decl *>::iterator i = AST->top_level_begin(), |
98 | e = AST->top_level_end(); |
99 | i != e; ++i) { |
100 | if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(Val: *i)) { |
101 | if (Record->getName() == "X" ) { |
102 | return true; |
103 | } |
104 | } |
105 | } |
106 | return false; |
107 | } |
108 | |
109 | struct TestDiagnosticConsumer : public DiagnosticConsumer { |
110 | TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {} |
111 | void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, |
112 | const Diagnostic &Info) override { |
113 | ++NumDiagnosticsSeen; |
114 | } |
115 | unsigned NumDiagnosticsSeen; |
116 | }; |
117 | } // end namespace |
118 | |
119 | TEST(runToolOnCode, FindsClassDecl) { |
120 | bool FoundClassDeclX = false; |
121 | EXPECT_TRUE(runToolOnCode( |
122 | std::make_unique<TestAction>( |
123 | std::make_unique<FindClassDeclXConsumer>(&FoundClassDeclX)), |
124 | "class X;" )); |
125 | EXPECT_TRUE(FoundClassDeclX); |
126 | |
127 | FoundClassDeclX = false; |
128 | EXPECT_TRUE(runToolOnCode( |
129 | std::make_unique<TestAction>( |
130 | std::make_unique<FindClassDeclXConsumer>(&FoundClassDeclX)), |
131 | "class Y;" )); |
132 | EXPECT_FALSE(FoundClassDeclX); |
133 | } |
134 | |
135 | TEST(buildASTFromCode, FindsClassDecl) { |
136 | std::unique_ptr<ASTUnit> AST = buildASTFromCode(Code: "class X;" ); |
137 | ASSERT_TRUE(AST.get()); |
138 | EXPECT_TRUE(FindClassDeclX(AST.get())); |
139 | |
140 | AST = buildASTFromCode(Code: "class Y;" ); |
141 | ASSERT_TRUE(AST.get()); |
142 | EXPECT_FALSE(FindClassDeclX(AST.get())); |
143 | } |
144 | |
145 | TEST(buildASTFromCode, ReportsErrors) { |
146 | TestDiagnosticConsumer Consumer; |
147 | std::unique_ptr<ASTUnit> AST = buildASTFromCodeWithArgs( |
148 | Code: "int x = \"A\";" , Args: {}, FileName: "input.cc" , ToolName: "clang-tool" , |
149 | PCHContainerOps: std::make_shared<PCHContainerOperations>(), |
150 | Adjuster: getClangStripDependencyFileAdjuster(), VirtualMappedFiles: FileContentMappings(), DiagConsumer: &Consumer); |
151 | EXPECT_TRUE(AST.get()); |
152 | EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); |
153 | } |
154 | |
155 | TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) { |
156 | std::unique_ptr<FrontendActionFactory> Factory( |
157 | newFrontendActionFactory<SyntaxOnlyAction>()); |
158 | std::unique_ptr<FrontendAction> Action(Factory->create()); |
159 | EXPECT_TRUE(Action.get() != nullptr); |
160 | } |
161 | |
162 | struct IndependentFrontendActionCreator { |
163 | std::unique_ptr<ASTConsumer> newASTConsumer() { |
164 | return std::make_unique<FindTopLevelDeclConsumer>(args: nullptr); |
165 | } |
166 | }; |
167 | |
168 | TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) { |
169 | IndependentFrontendActionCreator Creator; |
170 | std::unique_ptr<FrontendActionFactory> Factory( |
171 | newFrontendActionFactory(ConsumerFactory: &Creator)); |
172 | std::unique_ptr<FrontendAction> Action(Factory->create()); |
173 | EXPECT_TRUE(Action.get() != nullptr); |
174 | } |
175 | |
176 | TEST(ToolInvocation, TestMapVirtualFile) { |
177 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( |
178 | new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); |
179 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
180 | new llvm::vfs::InMemoryFileSystem); |
181 | OverlayFileSystem->pushOverlay(FS: InMemoryFileSystem); |
182 | llvm::IntrusiveRefCntPtr<FileManager> Files( |
183 | new FileManager(FileSystemOptions(), OverlayFileSystem)); |
184 | std::vector<std::string> Args; |
185 | Args.push_back(x: "tool-executable" ); |
186 | Args.push_back(x: "-Idef" ); |
187 | Args.push_back(x: "-fsyntax-only" ); |
188 | Args.push_back(x: "test.cpp" ); |
189 | clang::tooling::ToolInvocation Invocation( |
190 | Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); |
191 | InMemoryFileSystem->addFile( |
192 | Path: "test.cpp" , ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "#include <abc>\n" )); |
193 | InMemoryFileSystem->addFile(Path: "def/abc" , ModificationTime: 0, |
194 | Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "\n" )); |
195 | EXPECT_TRUE(Invocation.run()); |
196 | } |
197 | |
198 | TEST(ToolInvocation, TestVirtualModulesCompilation) { |
199 | // FIXME: Currently, this only tests that we don't exit with an error if a |
200 | // mapped module.modulemap is found on the include path. In the future, expand |
201 | // this test to run a full modules enabled compilation, so we make sure we can |
202 | // rerun modules compilations with a virtual file system. |
203 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( |
204 | new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); |
205 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
206 | new llvm::vfs::InMemoryFileSystem); |
207 | OverlayFileSystem->pushOverlay(FS: InMemoryFileSystem); |
208 | llvm::IntrusiveRefCntPtr<FileManager> Files( |
209 | new FileManager(FileSystemOptions(), OverlayFileSystem)); |
210 | std::vector<std::string> Args; |
211 | Args.push_back(x: "tool-executable" ); |
212 | Args.push_back(x: "-Idef" ); |
213 | Args.push_back(x: "-fsyntax-only" ); |
214 | Args.push_back(x: "test.cpp" ); |
215 | clang::tooling::ToolInvocation Invocation( |
216 | Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); |
217 | InMemoryFileSystem->addFile( |
218 | Path: "test.cpp" , ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "#include <abc>\n" )); |
219 | InMemoryFileSystem->addFile(Path: "def/abc" , ModificationTime: 0, |
220 | Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "\n" )); |
221 | // Add a module.modulemap file in the include directory of our header, so we |
222 | // trigger the module.modulemap header search logic. |
223 | InMemoryFileSystem->addFile(Path: "def/module.modulemap" , ModificationTime: 0, |
224 | Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "\n" )); |
225 | EXPECT_TRUE(Invocation.run()); |
226 | } |
227 | |
228 | TEST(ToolInvocation, DiagnosticsEngineProperlyInitializedForCC1Construction) { |
229 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( |
230 | new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); |
231 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
232 | new llvm::vfs::InMemoryFileSystem); |
233 | OverlayFileSystem->pushOverlay(FS: InMemoryFileSystem); |
234 | llvm::IntrusiveRefCntPtr<FileManager> Files( |
235 | new FileManager(FileSystemOptions(), OverlayFileSystem)); |
236 | |
237 | std::vector<std::string> Args; |
238 | Args.push_back(x: "tool-executable" ); |
239 | // Unknown warning option will result in a warning. |
240 | Args.push_back(x: "-fexpensive-optimizations" ); |
241 | // Argument that will suppress the warning above. |
242 | Args.push_back(x: "-Wno-ignored-optimization-argument" ); |
243 | Args.push_back(x: "-E" ); |
244 | Args.push_back(x: "test.cpp" ); |
245 | |
246 | clang::tooling::ToolInvocation Invocation( |
247 | Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); |
248 | InMemoryFileSystem->addFile(Path: "test.cpp" , ModificationTime: 0, |
249 | Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "" )); |
250 | TextDiagnosticBuffer Consumer; |
251 | Invocation.setDiagnosticConsumer(&Consumer); |
252 | EXPECT_TRUE(Invocation.run()); |
253 | // Check that the warning was ignored due to the '-Wno-xxx' argument. |
254 | EXPECT_EQ(std::distance(Consumer.warn_begin(), Consumer.warn_end()), 0u); |
255 | } |
256 | |
257 | TEST(ToolInvocation, CustomDiagnosticOptionsOverwriteParsedOnes) { |
258 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( |
259 | new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); |
260 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
261 | new llvm::vfs::InMemoryFileSystem); |
262 | OverlayFileSystem->pushOverlay(FS: InMemoryFileSystem); |
263 | llvm::IntrusiveRefCntPtr<FileManager> Files( |
264 | new FileManager(FileSystemOptions(), OverlayFileSystem)); |
265 | |
266 | std::vector<std::string> Args; |
267 | Args.push_back(x: "tool-executable" ); |
268 | // Unknown warning option will result in a warning. |
269 | Args.push_back(x: "-fexpensive-optimizations" ); |
270 | // Argument that will suppress the warning above. |
271 | Args.push_back(x: "-Wno-ignored-optimization-argument" ); |
272 | Args.push_back(x: "-E" ); |
273 | Args.push_back(x: "test.cpp" ); |
274 | |
275 | clang::tooling::ToolInvocation Invocation( |
276 | Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); |
277 | InMemoryFileSystem->addFile(Path: "test.cpp" , ModificationTime: 0, |
278 | Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "" )); |
279 | TextDiagnosticBuffer Consumer; |
280 | Invocation.setDiagnosticConsumer(&Consumer); |
281 | |
282 | // Inject custom `DiagnosticOptions` for command-line parsing. |
283 | auto DiagOpts = llvm::makeIntrusiveRefCnt<DiagnosticOptions>(); |
284 | Invocation.setDiagnosticOptions(&*DiagOpts); |
285 | |
286 | EXPECT_TRUE(Invocation.run()); |
287 | // Check that the warning was issued during command-line parsing due to the |
288 | // custom `DiagnosticOptions` without '-Wno-xxx'. |
289 | EXPECT_EQ(std::distance(Consumer.warn_begin(), Consumer.warn_end()), 1u); |
290 | } |
291 | |
292 | struct DiagnosticConsumerExpectingSourceManager : public DiagnosticConsumer { |
293 | bool SawSourceManager; |
294 | |
295 | DiagnosticConsumerExpectingSourceManager() : SawSourceManager(false) {} |
296 | |
297 | void HandleDiagnostic(clang::DiagnosticsEngine::Level, |
298 | const clang::Diagnostic &info) override { |
299 | SawSourceManager = info.hasSourceManager(); |
300 | } |
301 | }; |
302 | |
303 | TEST(ToolInvocation, DiagConsumerExpectingSourceManager) { |
304 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( |
305 | new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); |
306 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
307 | new llvm::vfs::InMemoryFileSystem); |
308 | OverlayFileSystem->pushOverlay(FS: InMemoryFileSystem); |
309 | llvm::IntrusiveRefCntPtr<FileManager> Files( |
310 | new FileManager(FileSystemOptions(), OverlayFileSystem)); |
311 | std::vector<std::string> Args; |
312 | Args.push_back(x: "tool-executable" ); |
313 | // Note: intentional error; user probably meant -ferror-limit=0. |
314 | Args.push_back(x: "-ferror-limit=-1" ); |
315 | Args.push_back(x: "-fsyntax-only" ); |
316 | Args.push_back(x: "test.cpp" ); |
317 | clang::tooling::ToolInvocation Invocation( |
318 | Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); |
319 | InMemoryFileSystem->addFile( |
320 | Path: "test.cpp" , ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "int main() {}\n" )); |
321 | |
322 | DiagnosticConsumerExpectingSourceManager Consumer; |
323 | Invocation.setDiagnosticConsumer(&Consumer); |
324 | |
325 | EXPECT_TRUE(Invocation.run()); |
326 | EXPECT_TRUE(Consumer.SawSourceManager); |
327 | } |
328 | |
329 | TEST(ToolInvocation, CC1Args) { |
330 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( |
331 | new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); |
332 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
333 | new llvm::vfs::InMemoryFileSystem); |
334 | OverlayFileSystem->pushOverlay(FS: InMemoryFileSystem); |
335 | llvm::IntrusiveRefCntPtr<FileManager> Files( |
336 | new FileManager(FileSystemOptions(), OverlayFileSystem)); |
337 | std::vector<std::string> Args; |
338 | Args.push_back(x: "tool-executable" ); |
339 | Args.push_back(x: "-cc1" ); |
340 | Args.push_back(x: "-fsyntax-only" ); |
341 | Args.push_back(x: "test.cpp" ); |
342 | clang::tooling::ToolInvocation Invocation( |
343 | Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); |
344 | InMemoryFileSystem->addFile( |
345 | Path: "test.cpp" , ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "void foo(void);\n" )); |
346 | EXPECT_TRUE(Invocation.run()); |
347 | } |
348 | |
349 | TEST(ToolInvocation, CC1ArgsInvalid) { |
350 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( |
351 | new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); |
352 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
353 | new llvm::vfs::InMemoryFileSystem); |
354 | OverlayFileSystem->pushOverlay(FS: InMemoryFileSystem); |
355 | llvm::IntrusiveRefCntPtr<FileManager> Files( |
356 | new FileManager(FileSystemOptions(), OverlayFileSystem)); |
357 | std::vector<std::string> Args; |
358 | Args.push_back(x: "tool-executable" ); |
359 | Args.push_back(x: "-cc1" ); |
360 | Args.push_back(x: "-invalid-arg" ); |
361 | Args.push_back(x: "test.cpp" ); |
362 | clang::tooling::ToolInvocation Invocation( |
363 | Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); |
364 | InMemoryFileSystem->addFile( |
365 | Path: "test.cpp" , ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "void foo(void);\n" )); |
366 | EXPECT_FALSE(Invocation.run()); |
367 | } |
368 | |
369 | namespace { |
370 | /// Overlays the real filesystem with the given VFS and returns the result. |
371 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> |
372 | overlayRealFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { |
373 | auto RFS = llvm::vfs::getRealFileSystem(); |
374 | auto OverlayFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(A&: RFS); |
375 | OverlayFS->pushOverlay(FS: VFS); |
376 | return OverlayFS; |
377 | } |
378 | |
379 | struct CommandLineExtractorTest : public ::testing::Test { |
380 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFS; |
381 | llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags; |
382 | driver::Driver Driver; |
383 | |
384 | public: |
385 | CommandLineExtractorTest() |
386 | : InMemoryFS(new llvm::vfs::InMemoryFileSystem), |
387 | Diags(CompilerInstance::createDiagnostics(Opts: new DiagnosticOptions)), |
388 | Driver("clang" , llvm::sys::getDefaultTargetTriple(), *Diags, |
389 | "clang LLVM compiler" , overlayRealFS(VFS: InMemoryFS)) {} |
390 | |
391 | void addFile(StringRef Name, StringRef Content) { |
392 | InMemoryFS->addFile(Path: Name, ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: Content)); |
393 | } |
394 | |
395 | const llvm::opt::ArgStringList * |
396 | extractCC1Arguments(llvm::ArrayRef<const char *> Argv) { |
397 | const std::unique_ptr<driver::Compilation> Compilation( |
398 | Driver.BuildCompilation(Args: llvm::ArrayRef(Argv))); |
399 | |
400 | return getCC1Arguments(Diagnostics: Diags.get(), Compilation: Compilation.get()); |
401 | } |
402 | }; |
403 | } // namespace |
404 | |
405 | TEST_F(CommandLineExtractorTest, AcceptOffloading) { |
406 | addFile(Name: "test.c" , Content: "int main() {}\n" ); |
407 | const char *Args[] = {"clang" , "-target" , "arm64-apple-macosx11.0.0" , |
408 | "-x" , "hip" , "test.c" , |
409 | "-nogpulib" , "-nogpuinc" }; |
410 | EXPECT_NE(extractCC1Arguments(Args), nullptr); |
411 | } |
412 | |
413 | TEST_F(CommandLineExtractorTest, AcceptOffloadingCompile) { |
414 | addFile(Name: "test.c" , Content: "int main() {}\n" ); |
415 | const char *Args[] = {"clang" , "-target" , "arm64-apple-macosx11.0.0" , |
416 | "-c" , "-x" , "hip" , |
417 | "test.c" , "-nogpulib" , "-nogpuinc" }; |
418 | EXPECT_NE(extractCC1Arguments(Args), nullptr); |
419 | } |
420 | |
421 | TEST_F(CommandLineExtractorTest, AcceptOffloadingSyntaxOnly) { |
422 | addFile(Name: "test.c" , Content: "int main() {}\n" ); |
423 | const char *Args[] = { |
424 | "clang" , "-target" , "arm64-apple-macosx11.0.0" , |
425 | "-fsyntax-only" , "-x" , "hip" , |
426 | "test.c" , "-nogpulib" , "-nogpuinc" }; |
427 | EXPECT_NE(extractCC1Arguments(Args), nullptr); |
428 | } |
429 | |
430 | TEST_F(CommandLineExtractorTest, AcceptExternalAssembler) { |
431 | addFile(Name: "test.c" , Content: "int main() {}\n" ); |
432 | const char *Args[] = { |
433 | "clang" , "-target" , "arm64-apple-macosx11.0.0" , "-fno-integrated-as" , |
434 | "-c" , "test.c" }; |
435 | EXPECT_NE(extractCC1Arguments(Args), nullptr); |
436 | } |
437 | |
438 | TEST_F(CommandLineExtractorTest, AcceptEmbedBitcode) { |
439 | addFile(Name: "test.c" , Content: "int main() {}\n" ); |
440 | const char *Args[] = {"clang" , "-target" , "arm64-apple-macosx11.0.0" , |
441 | "-c" , "-fembed-bitcode" , "test.c" }; |
442 | EXPECT_NE(extractCC1Arguments(Args), nullptr); |
443 | } |
444 | |
445 | TEST_F(CommandLineExtractorTest, AcceptSaveTemps) { |
446 | addFile(Name: "test.c" , Content: "int main() {}\n" ); |
447 | const char *Args[] = {"clang" , "-target" , "arm64-apple-macosx11.0.0" , |
448 | "-c" , "-save-temps" , "test.c" }; |
449 | EXPECT_NE(extractCC1Arguments(Args), nullptr); |
450 | } |
451 | |
452 | TEST_F(CommandLineExtractorTest, AcceptPreprocessedInputFile) { |
453 | addFile(Name: "test.i" , Content: "int main() {}\n" ); |
454 | const char *Args[] = {"clang" , "-target" , "arm64-apple-macosx11.0.0" , "-c" , |
455 | "test.i" }; |
456 | EXPECT_NE(extractCC1Arguments(Args), nullptr); |
457 | } |
458 | |
459 | TEST_F(CommandLineExtractorTest, RejectMultipleArchitectures) { |
460 | addFile(Name: "test.c" , Content: "int main() {}\n" ); |
461 | const char *Args[] = {"clang" , "-target" , "arm64-apple-macosx11.0.0" , |
462 | "-arch" , "x86_64" , "-arch" , |
463 | "arm64" , "-c" , "test.c" }; |
464 | EXPECT_EQ(extractCC1Arguments(Args), nullptr); |
465 | } |
466 | |
467 | TEST_F(CommandLineExtractorTest, RejectMultipleInputFiles) { |
468 | addFile(Name: "one.c" , Content: "void one() {}\n" ); |
469 | addFile(Name: "two.c" , Content: "void two() {}\n" ); |
470 | const char *Args[] = {"clang" , "-target" , "arm64-apple-macosx11.0.0" , |
471 | "-c" , "one.c" , "two.c" }; |
472 | EXPECT_EQ(extractCC1Arguments(Args), nullptr); |
473 | } |
474 | |
475 | struct VerifyEndCallback : public SourceFileCallbacks { |
476 | VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {} |
477 | bool handleBeginSource(CompilerInstance &CI) override { |
478 | ++BeginCalled; |
479 | return true; |
480 | } |
481 | void handleEndSource() override { ++EndCalled; } |
482 | std::unique_ptr<ASTConsumer> newASTConsumer() { |
483 | return std::make_unique<FindTopLevelDeclConsumer>(args: &Matched); |
484 | } |
485 | unsigned BeginCalled; |
486 | unsigned EndCalled; |
487 | bool Matched; |
488 | }; |
489 | |
490 | #if !defined(_WIN32) |
491 | TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) { |
492 | VerifyEndCallback EndCallback; |
493 | |
494 | FixedCompilationDatabase Compilations("/" , std::vector<std::string>()); |
495 | std::vector<std::string> Sources; |
496 | Sources.push_back(x: "/a.cc" ); |
497 | Sources.push_back(x: "/b.cc" ); |
498 | ClangTool Tool(Compilations, Sources); |
499 | |
500 | Tool.mapVirtualFile(FilePath: "/a.cc" , Content: "void a() {}" ); |
501 | Tool.mapVirtualFile(FilePath: "/b.cc" , Content: "void b() {}" ); |
502 | |
503 | std::unique_ptr<FrontendActionFactory> Action( |
504 | newFrontendActionFactory(ConsumerFactory: &EndCallback, Callbacks: &EndCallback)); |
505 | Tool.run(Action: Action.get()); |
506 | |
507 | EXPECT_TRUE(EndCallback.Matched); |
508 | EXPECT_EQ(2u, EndCallback.BeginCalled); |
509 | EXPECT_EQ(2u, EndCallback.EndCalled); |
510 | } |
511 | #endif |
512 | |
513 | struct SkipBodyConsumer : public clang::ASTConsumer { |
514 | /// Skip the 'skipMe' function. |
515 | bool shouldSkipFunctionBody(Decl *D) override { |
516 | NamedDecl *F = dyn_cast<NamedDecl>(Val: D); |
517 | return F && F->getNameAsString() == "skipMe" ; |
518 | } |
519 | }; |
520 | |
521 | struct SkipBodyAction : public clang::ASTFrontendAction { |
522 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, |
523 | StringRef) override { |
524 | Compiler.getFrontendOpts().SkipFunctionBodies = true; |
525 | return std::make_unique<SkipBodyConsumer>(); |
526 | } |
527 | }; |
528 | |
529 | TEST(runToolOnCode, TestSkipFunctionBody) { |
530 | std::vector<std::string> Args = {"-std=c++11" }; |
531 | std::vector<std::string> Args2 = {"-fno-delayed-template-parsing" }; |
532 | |
533 | EXPECT_TRUE(runToolOnCode(std::make_unique<SkipBodyAction>(), |
534 | "int skipMe() { an_error_here }" )); |
535 | EXPECT_FALSE(runToolOnCode(std::make_unique<SkipBodyAction>(), |
536 | "int skipMeNot() { an_error_here }" )); |
537 | |
538 | // Test constructors with initializers |
539 | EXPECT_TRUE(runToolOnCodeWithArgs( |
540 | std::make_unique<SkipBodyAction>(), |
541 | "struct skipMe { skipMe() : an_error() { more error } };" , Args)); |
542 | EXPECT_TRUE(runToolOnCodeWithArgs( |
543 | std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };" |
544 | "skipMe::skipMe() : an_error([](){;}) { more error }" , |
545 | Args)); |
546 | EXPECT_TRUE(runToolOnCodeWithArgs( |
547 | std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };" |
548 | "skipMe::skipMe() : an_error{[](){;}} { more error }" , |
549 | Args)); |
550 | EXPECT_TRUE(runToolOnCodeWithArgs( |
551 | std::make_unique<SkipBodyAction>(), |
552 | "struct skipMe { skipMe(); };" |
553 | "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }" , |
554 | Args)); |
555 | EXPECT_TRUE(runToolOnCodeWithArgs( |
556 | std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe() : bases()... { error } };" , |
557 | Args)); |
558 | |
559 | EXPECT_FALSE(runToolOnCodeWithArgs( |
560 | std::make_unique<SkipBodyAction>(), "struct skipMeNot { skipMeNot() : an_error() { } };" , |
561 | Args)); |
562 | EXPECT_FALSE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), |
563 | "struct skipMeNot { skipMeNot(); };" |
564 | "skipMeNot::skipMeNot() : an_error() { }" , |
565 | Args)); |
566 | |
567 | // Try/catch |
568 | EXPECT_TRUE(runToolOnCode( |
569 | std::make_unique<SkipBodyAction>(), |
570 | "void skipMe() try { an_error() } catch(error) { error };" )); |
571 | EXPECT_TRUE(runToolOnCode( |
572 | std::make_unique<SkipBodyAction>(), |
573 | "struct S { void skipMe() try { an_error() } catch(error) { error } };" )); |
574 | EXPECT_TRUE( |
575 | runToolOnCode(std::make_unique<SkipBodyAction>(), |
576 | "void skipMe() try { an_error() } catch(error) { error; }" |
577 | "catch(error) { error } catch (error) { }" )); |
578 | EXPECT_FALSE(runToolOnCode( |
579 | std::make_unique<SkipBodyAction>(), |
580 | "void skipMe() try something;" )); // don't crash while parsing |
581 | |
582 | // Template |
583 | EXPECT_TRUE(runToolOnCode( |
584 | std::make_unique<SkipBodyAction>(), "template<typename T> int skipMe() { an_error_here }" |
585 | "int x = skipMe<int>();" )); |
586 | EXPECT_FALSE(runToolOnCodeWithArgs( |
587 | std::make_unique<SkipBodyAction>(), |
588 | "template<typename T> int skipMeNot() { an_error_here }" , Args2)); |
589 | } |
590 | |
591 | TEST(runToolOnCodeWithArgs, TestNoDepFile) { |
592 | llvm::SmallString<32> DepFilePath; |
593 | ASSERT_FALSE(llvm::sys::fs::getPotentiallyUniqueTempFileName("depfile" , "d" , |
594 | DepFilePath)); |
595 | std::vector<std::string> Args; |
596 | Args.push_back(x: "-MMD" ); |
597 | Args.push_back(x: "-MT" ); |
598 | Args.push_back(x: std::string(DepFilePath.str())); |
599 | Args.push_back(x: "-MF" ); |
600 | Args.push_back(x: std::string(DepFilePath.str())); |
601 | EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), "" , Args)); |
602 | EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str())); |
603 | EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str())); |
604 | } |
605 | |
606 | struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction { |
607 | CheckColoredDiagnosticsAction(bool ShouldShowColor) |
608 | : ShouldShowColor(ShouldShowColor) {} |
609 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, |
610 | StringRef) override { |
611 | if (Compiler.getDiagnosticOpts().ShowColors != ShouldShowColor) |
612 | Compiler.getDiagnostics().Report( |
613 | DiagID: Compiler.getDiagnostics().getCustomDiagID( |
614 | L: DiagnosticsEngine::Fatal, |
615 | FormatString: "getDiagnosticOpts().ShowColors != ShouldShowColor" )); |
616 | return std::make_unique<ASTConsumer>(); |
617 | } |
618 | |
619 | private: |
620 | bool ShouldShowColor = true; |
621 | }; |
622 | |
623 | TEST(runToolOnCodeWithArgs, DiagnosticsColor) { |
624 | EXPECT_TRUE(runToolOnCodeWithArgs( |
625 | std::make_unique<CheckColoredDiagnosticsAction>(true), "" , |
626 | {"-fcolor-diagnostics" })); |
627 | EXPECT_TRUE(runToolOnCodeWithArgs( |
628 | std::make_unique<CheckColoredDiagnosticsAction>(false), "" , |
629 | {"-fno-color-diagnostics" })); |
630 | EXPECT_TRUE(runToolOnCodeWithArgs( |
631 | std::make_unique<CheckColoredDiagnosticsAction>(true), "" , |
632 | {"-fno-color-diagnostics" , "-fcolor-diagnostics" })); |
633 | EXPECT_TRUE(runToolOnCodeWithArgs( |
634 | std::make_unique<CheckColoredDiagnosticsAction>(false), "" , |
635 | {"-fcolor-diagnostics" , "-fno-color-diagnostics" })); |
636 | EXPECT_TRUE(runToolOnCodeWithArgs( |
637 | std::make_unique<CheckColoredDiagnosticsAction>(true), "" , |
638 | {"-fno-color-diagnostics" , "-fdiagnostics-color=always" })); |
639 | |
640 | // Check that this test would fail if ShowColors is not what it should. |
641 | EXPECT_FALSE(runToolOnCodeWithArgs( |
642 | std::make_unique<CheckColoredDiagnosticsAction>(false), "" , |
643 | {"-fcolor-diagnostics" })); |
644 | } |
645 | |
646 | TEST(ClangToolTest, ArgumentAdjusters) { |
647 | FixedCompilationDatabase Compilations("/" , std::vector<std::string>()); |
648 | |
649 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc" )); |
650 | Tool.mapVirtualFile(FilePath: "/a.cc" , Content: "void a() {}" ); |
651 | |
652 | std::unique_ptr<FrontendActionFactory> Action( |
653 | newFrontendActionFactory<SyntaxOnlyAction>()); |
654 | |
655 | bool Found = false; |
656 | bool Ran = false; |
657 | ArgumentsAdjuster CheckSyntaxOnlyAdjuster = |
658 | [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) { |
659 | Ran = true; |
660 | if (llvm::is_contained(Range: Args, Element: "-fsyntax-only" )) |
661 | Found = true; |
662 | return Args; |
663 | }; |
664 | Tool.appendArgumentsAdjuster(Adjuster: CheckSyntaxOnlyAdjuster); |
665 | Tool.run(Action: Action.get()); |
666 | EXPECT_TRUE(Ran); |
667 | EXPECT_TRUE(Found); |
668 | |
669 | Ran = Found = false; |
670 | Tool.clearArgumentsAdjusters(); |
671 | Tool.appendArgumentsAdjuster(Adjuster: CheckSyntaxOnlyAdjuster); |
672 | Tool.appendArgumentsAdjuster(Adjuster: getClangSyntaxOnlyAdjuster()); |
673 | Tool.run(Action: Action.get()); |
674 | EXPECT_TRUE(Ran); |
675 | EXPECT_FALSE(Found); |
676 | } |
677 | |
678 | TEST(ClangToolTest, NoDoubleSyntaxOnly) { |
679 | FixedCompilationDatabase Compilations("/" , {"-fsyntax-only" }); |
680 | |
681 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc" )); |
682 | Tool.mapVirtualFile(FilePath: "/a.cc" , Content: "void a() {}" ); |
683 | |
684 | std::unique_ptr<FrontendActionFactory> Action( |
685 | newFrontendActionFactory<SyntaxOnlyAction>()); |
686 | |
687 | size_t SyntaxOnlyCount = 0; |
688 | ArgumentsAdjuster CheckSyntaxOnlyAdjuster = |
689 | [&SyntaxOnlyCount](const CommandLineArguments &Args, |
690 | StringRef /*unused*/) { |
691 | for (llvm::StringRef Arg : Args) { |
692 | if (Arg == "-fsyntax-only" ) |
693 | ++SyntaxOnlyCount; |
694 | } |
695 | return Args; |
696 | }; |
697 | |
698 | Tool.clearArgumentsAdjusters(); |
699 | Tool.appendArgumentsAdjuster(Adjuster: getClangSyntaxOnlyAdjuster()); |
700 | Tool.appendArgumentsAdjuster(Adjuster: CheckSyntaxOnlyAdjuster); |
701 | Tool.run(Action: Action.get()); |
702 | EXPECT_EQ(SyntaxOnlyCount, 1U); |
703 | } |
704 | |
705 | TEST(ClangToolTest, NoOutputCommands) { |
706 | FixedCompilationDatabase Compilations("/" , {"-save-temps" , "-save-temps=cwd" , |
707 | "--save-temps" , |
708 | "--save-temps=somedir" }); |
709 | |
710 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc" )); |
711 | Tool.mapVirtualFile(FilePath: "/a.cc" , Content: "void a() {}" ); |
712 | |
713 | std::unique_ptr<FrontendActionFactory> Action( |
714 | newFrontendActionFactory<SyntaxOnlyAction>()); |
715 | |
716 | const std::vector<llvm::StringRef> OutputCommands = {"-save-temps" }; |
717 | bool Ran = false; |
718 | ArgumentsAdjuster CheckSyntaxOnlyAdjuster = |
719 | [&OutputCommands, &Ran](const CommandLineArguments &Args, |
720 | StringRef /*unused*/) { |
721 | for (llvm::StringRef Arg : Args) { |
722 | for (llvm::StringRef OutputCommand : OutputCommands) |
723 | EXPECT_FALSE(Arg.contains(OutputCommand)); |
724 | } |
725 | Ran = true; |
726 | return Args; |
727 | }; |
728 | |
729 | Tool.clearArgumentsAdjusters(); |
730 | Tool.appendArgumentsAdjuster(Adjuster: getClangSyntaxOnlyAdjuster()); |
731 | Tool.appendArgumentsAdjuster(Adjuster: CheckSyntaxOnlyAdjuster); |
732 | Tool.run(Action: Action.get()); |
733 | EXPECT_TRUE(Ran); |
734 | } |
735 | |
736 | TEST(ClangToolTest, BaseVirtualFileSystemUsage) { |
737 | FixedCompilationDatabase Compilations("/" , std::vector<std::string>()); |
738 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( |
739 | new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); |
740 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
741 | new llvm::vfs::InMemoryFileSystem); |
742 | OverlayFileSystem->pushOverlay(FS: InMemoryFileSystem); |
743 | |
744 | InMemoryFileSystem->addFile( |
745 | Path: "a.cpp" , ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "int main() {}" )); |
746 | |
747 | ClangTool Tool(Compilations, std::vector<std::string>(1, "a.cpp" ), |
748 | std::make_shared<PCHContainerOperations>(), OverlayFileSystem); |
749 | std::unique_ptr<FrontendActionFactory> Action( |
750 | newFrontendActionFactory<SyntaxOnlyAction>()); |
751 | EXPECT_EQ(0, Tool.run(Action.get())); |
752 | } |
753 | |
754 | // Check getClangStripDependencyFileAdjuster doesn't strip args after -MD/-MMD. |
755 | TEST(ClangToolTest, StripDependencyFileAdjuster) { |
756 | FixedCompilationDatabase Compilations("/" , {"-MD" , "-c" , "-MMD" , "-w" }); |
757 | |
758 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc" )); |
759 | Tool.mapVirtualFile(FilePath: "/a.cc" , Content: "void a() {}" ); |
760 | |
761 | std::unique_ptr<FrontendActionFactory> Action( |
762 | newFrontendActionFactory<SyntaxOnlyAction>()); |
763 | |
764 | CommandLineArguments FinalArgs; |
765 | ArgumentsAdjuster CheckFlagsAdjuster = |
766 | [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { |
767 | FinalArgs = Args; |
768 | return Args; |
769 | }; |
770 | Tool.clearArgumentsAdjusters(); |
771 | Tool.appendArgumentsAdjuster(Adjuster: getClangStripDependencyFileAdjuster()); |
772 | Tool.appendArgumentsAdjuster(Adjuster: CheckFlagsAdjuster); |
773 | Tool.run(Action: Action.get()); |
774 | |
775 | auto HasFlag = [&FinalArgs](const std::string &Flag) { |
776 | return llvm::find(Range&: FinalArgs, Val: Flag) != FinalArgs.end(); |
777 | }; |
778 | EXPECT_FALSE(HasFlag("-MD" )); |
779 | EXPECT_FALSE(HasFlag("-MMD" )); |
780 | EXPECT_TRUE(HasFlag("-c" )); |
781 | EXPECT_TRUE(HasFlag("-w" )); |
782 | } |
783 | |
784 | // Check getClangStripDependencyFileAdjuster strips /showIncludes and variants |
785 | TEST(ClangToolTest, StripDependencyFileAdjusterShowIncludes) { |
786 | FixedCompilationDatabase Compilations( |
787 | "/" , {"/showIncludes" , "/showIncludes:user" , "-showIncludes" , |
788 | "-showIncludes:user" , "-c" }); |
789 | |
790 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc" )); |
791 | Tool.mapVirtualFile(FilePath: "/a.cc" , Content: "void a() {}" ); |
792 | |
793 | std::unique_ptr<FrontendActionFactory> Action( |
794 | newFrontendActionFactory<SyntaxOnlyAction>()); |
795 | |
796 | CommandLineArguments FinalArgs; |
797 | ArgumentsAdjuster CheckFlagsAdjuster = |
798 | [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { |
799 | FinalArgs = Args; |
800 | return Args; |
801 | }; |
802 | Tool.clearArgumentsAdjusters(); |
803 | Tool.appendArgumentsAdjuster(Adjuster: getClangStripDependencyFileAdjuster()); |
804 | Tool.appendArgumentsAdjuster(Adjuster: CheckFlagsAdjuster); |
805 | Tool.run(Action: Action.get()); |
806 | |
807 | auto HasFlag = [&FinalArgs](const std::string &Flag) { |
808 | return llvm::find(Range&: FinalArgs, Val: Flag) != FinalArgs.end(); |
809 | }; |
810 | EXPECT_FALSE(HasFlag("/showIncludes" )); |
811 | EXPECT_FALSE(HasFlag("/showIncludes:user" )); |
812 | EXPECT_FALSE(HasFlag("-showIncludes" )); |
813 | EXPECT_FALSE(HasFlag("-showIncludes:user" )); |
814 | EXPECT_TRUE(HasFlag("-c" )); |
815 | } |
816 | |
817 | // Check getClangStripDependencyFileAdjuster doesn't strip args when using the |
818 | // MSVC cl.exe driver |
819 | TEST(ClangToolTest, StripDependencyFileAdjusterMsvc) { |
820 | FixedCompilationDatabase Compilations( |
821 | "/" , {"--driver-mode=cl" , "-MD" , "-MDd" , "-MT" , "-O1" , "-MTd" , "-MP" }); |
822 | |
823 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc" )); |
824 | Tool.mapVirtualFile(FilePath: "/a.cc" , Content: "void a() {}" ); |
825 | |
826 | std::unique_ptr<FrontendActionFactory> Action( |
827 | newFrontendActionFactory<SyntaxOnlyAction>()); |
828 | |
829 | CommandLineArguments FinalArgs; |
830 | ArgumentsAdjuster CheckFlagsAdjuster = |
831 | [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { |
832 | FinalArgs = Args; |
833 | return Args; |
834 | }; |
835 | Tool.clearArgumentsAdjusters(); |
836 | Tool.appendArgumentsAdjuster(Adjuster: getClangStripDependencyFileAdjuster()); |
837 | Tool.appendArgumentsAdjuster(Adjuster: CheckFlagsAdjuster); |
838 | Tool.run(Action: Action.get()); |
839 | |
840 | auto HasFlag = [&FinalArgs](const std::string &Flag) { |
841 | return llvm::find(Range&: FinalArgs, Val: Flag) != FinalArgs.end(); |
842 | }; |
843 | EXPECT_TRUE(HasFlag("-MD" )); |
844 | EXPECT_TRUE(HasFlag("-MDd" )); |
845 | EXPECT_TRUE(HasFlag("-MT" )); |
846 | EXPECT_TRUE(HasFlag("-O1" )); |
847 | EXPECT_TRUE(HasFlag("-MTd" )); |
848 | EXPECT_TRUE(HasFlag("-MP" )); |
849 | } |
850 | |
851 | // Check getClangStripPluginsAdjuster strips plugin related args. |
852 | TEST(ClangToolTest, StripPluginsAdjuster) { |
853 | FixedCompilationDatabase Compilations( |
854 | "/" , {"-Xclang" , "-add-plugin" , "-Xclang" , "random-plugin" }); |
855 | |
856 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc" )); |
857 | Tool.mapVirtualFile(FilePath: "/a.cc" , Content: "void a() {}" ); |
858 | |
859 | std::unique_ptr<FrontendActionFactory> Action( |
860 | newFrontendActionFactory<SyntaxOnlyAction>()); |
861 | |
862 | CommandLineArguments FinalArgs; |
863 | ArgumentsAdjuster CheckFlagsAdjuster = |
864 | [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { |
865 | FinalArgs = Args; |
866 | return Args; |
867 | }; |
868 | Tool.clearArgumentsAdjusters(); |
869 | Tool.appendArgumentsAdjuster(Adjuster: getStripPluginsAdjuster()); |
870 | Tool.appendArgumentsAdjuster(Adjuster: CheckFlagsAdjuster); |
871 | Tool.run(Action: Action.get()); |
872 | |
873 | auto HasFlag = [&FinalArgs](const std::string &Flag) { |
874 | return llvm::find(Range&: FinalArgs, Val: Flag) != FinalArgs.end(); |
875 | }; |
876 | EXPECT_FALSE(HasFlag("-Xclang" )); |
877 | EXPECT_FALSE(HasFlag("-add-plugin" )); |
878 | EXPECT_FALSE(HasFlag("-random-plugin" )); |
879 | } |
880 | |
881 | TEST(addTargetAndModeForProgramName, AddsTargetAndMode) { |
882 | llvm::InitializeAllTargets(); |
883 | |
884 | std::string Target = getAnyTargetForTesting(); |
885 | ASSERT_FALSE(Target.empty()); |
886 | |
887 | std::vector<std::string> Args = {"clang" , "-foo" }; |
888 | addTargetAndModeForProgramName(CommandLine&: Args, InvokedAs: "" ); |
889 | EXPECT_EQ((std::vector<std::string>{"clang" , "-foo" }), Args); |
890 | addTargetAndModeForProgramName(CommandLine&: Args, InvokedAs: Target + "-g++" ); |
891 | EXPECT_EQ((std::vector<std::string>{"clang" , "--target=" + Target, |
892 | "--driver-mode=g++" , "-foo" }), |
893 | Args); |
894 | } |
895 | |
896 | TEST(addTargetAndModeForProgramName, PathIgnored) { |
897 | llvm::InitializeAllTargets(); |
898 | std::string Target = getAnyTargetForTesting(); |
899 | ASSERT_FALSE(Target.empty()); |
900 | |
901 | SmallString<32> ToolPath; |
902 | llvm::sys::path::append(path&: ToolPath, a: "foo" , b: "bar" , c: Target + "-g++" ); |
903 | |
904 | std::vector<std::string> Args = {"clang" , "-foo" }; |
905 | addTargetAndModeForProgramName(CommandLine&: Args, InvokedAs: ToolPath); |
906 | EXPECT_EQ((std::vector<std::string>{"clang" , "--target=" + Target, |
907 | "--driver-mode=g++" , "-foo" }), |
908 | Args); |
909 | } |
910 | |
911 | TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) { |
912 | llvm::InitializeAllTargets(); |
913 | std::string Target = getAnyTargetForTesting(); |
914 | ASSERT_FALSE(Target.empty()); |
915 | |
916 | std::vector<std::string> Args = {"clang" , "-foo" , "-target" , "something" }; |
917 | addTargetAndModeForProgramName(CommandLine&: Args, InvokedAs: Target + "-g++" ); |
918 | EXPECT_EQ((std::vector<std::string>{"clang" , "--driver-mode=g++" , "-foo" , |
919 | "-target" , "something" }), |
920 | Args); |
921 | |
922 | std::vector<std::string> ArgsAlt = {"clang" , "-foo" , "--target=something" }; |
923 | addTargetAndModeForProgramName(CommandLine&: ArgsAlt, InvokedAs: Target + "-g++" ); |
924 | EXPECT_EQ((std::vector<std::string>{"clang" , "--driver-mode=g++" , "-foo" , |
925 | "--target=something" }), |
926 | ArgsAlt); |
927 | } |
928 | |
929 | TEST(addTargetAndModeForProgramName, IgnoresExistingMode) { |
930 | llvm::InitializeAllTargets(); |
931 | std::string Target = getAnyTargetForTesting(); |
932 | ASSERT_FALSE(Target.empty()); |
933 | |
934 | std::vector<std::string> Args = {"clang" , "-foo" , "--driver-mode=abc" }; |
935 | addTargetAndModeForProgramName(CommandLine&: Args, InvokedAs: Target + "-g++" ); |
936 | EXPECT_EQ((std::vector<std::string>{"clang" , "--target=" + Target, "-foo" , |
937 | "--driver-mode=abc" }), |
938 | Args); |
939 | } |
940 | |
941 | #ifndef _WIN32 |
942 | TEST(ClangToolTest, BuildASTs) { |
943 | FixedCompilationDatabase Compilations("/" , std::vector<std::string>()); |
944 | |
945 | std::vector<std::string> Sources; |
946 | Sources.push_back(x: "/a.cc" ); |
947 | Sources.push_back(x: "/b.cc" ); |
948 | ClangTool Tool(Compilations, Sources); |
949 | |
950 | Tool.mapVirtualFile(FilePath: "/a.cc" , Content: "void a() {}" ); |
951 | Tool.mapVirtualFile(FilePath: "/b.cc" , Content: "void b() {}" ); |
952 | |
953 | std::vector<std::unique_ptr<ASTUnit>> ASTs; |
954 | EXPECT_EQ(0, Tool.buildASTs(ASTs)); |
955 | EXPECT_EQ(2u, ASTs.size()); |
956 | } |
957 | |
958 | TEST(ClangToolTest, InjectDiagnosticConsumer) { |
959 | FixedCompilationDatabase Compilations("/" , std::vector<std::string>()); |
960 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc" )); |
961 | Tool.mapVirtualFile(FilePath: "/a.cc" , Content: "int x = undeclared;" ); |
962 | TestDiagnosticConsumer Consumer; |
963 | Tool.setDiagnosticConsumer(&Consumer); |
964 | std::unique_ptr<FrontendActionFactory> Action( |
965 | newFrontendActionFactory<SyntaxOnlyAction>()); |
966 | Tool.run(Action: Action.get()); |
967 | EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); |
968 | } |
969 | |
970 | TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) { |
971 | FixedCompilationDatabase Compilations("/" , std::vector<std::string>()); |
972 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc" )); |
973 | Tool.mapVirtualFile(FilePath: "/a.cc" , Content: "int x = undeclared;" ); |
974 | TestDiagnosticConsumer Consumer; |
975 | Tool.setDiagnosticConsumer(&Consumer); |
976 | std::vector<std::unique_ptr<ASTUnit>> ASTs; |
977 | Tool.buildASTs(ASTs); |
978 | EXPECT_EQ(1u, ASTs.size()); |
979 | EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); |
980 | } |
981 | #endif |
982 | |
983 | TEST(runToolOnCode, TestResetDiagnostics) { |
984 | // This is a tool that resets the diagnostic during the compilation. |
985 | struct ResetDiagnosticAction : public clang::ASTFrontendAction { |
986 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, |
987 | StringRef) override { |
988 | struct Consumer : public clang::ASTConsumer { |
989 | bool HandleTopLevelDecl(clang::DeclGroupRef D) override { |
990 | auto &Diags = (*D.begin())->getASTContext().getDiagnostics(); |
991 | // Ignore any error |
992 | Diags.Reset(); |
993 | // Disable warnings because computing the CFG might crash. |
994 | Diags.setIgnoreAllWarnings(true); |
995 | return true; |
996 | } |
997 | }; |
998 | return std::make_unique<Consumer>(); |
999 | } |
1000 | }; |
1001 | |
1002 | // Should not crash |
1003 | EXPECT_FALSE( |
1004 | runToolOnCode(std::make_unique<ResetDiagnosticAction>(), |
1005 | "struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };" |
1006 | "void func() { long x; Foo f(x); }" )); |
1007 | } |
1008 | |
1009 | } // end namespace tooling |
1010 | } // end namespace clang |
1011 | |