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