1 //===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "clang/AST/ASTConsumer.h"
11 #include "clang/AST/DeclCXX.h"
12 #include "clang/AST/DeclGroup.h"
13 #include "clang/Frontend/ASTUnit.h"
14 #include "clang/Frontend/CompilerInstance.h"
15 #include "clang/Frontend/FrontendAction.h"
16 #include "clang/Frontend/FrontendActions.h"
17 #include "clang/Tooling/CompilationDatabase.h"
18 #include "clang/Tooling/Tooling.h"
19 #include "llvm/ADT/STLExtras.h"
20 #include "llvm/Config/llvm-config.h"
21 #include "gtest/gtest.h"
29 /// Takes an ast consumer and returns it from CreateASTConsumer. This only
30 /// works with single translation unit compilations.
31 class TestAction : public clang::ASTFrontendAction {
33 /// Takes ownership of TestConsumer.
34 explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer)
35 : TestConsumer(std::move(TestConsumer)) {}
38 virtual std::unique_ptr<clang::ASTConsumer>
39 CreateASTConsumer(clang::CompilerInstance &compiler, StringRef dummy) {
40 /// TestConsumer will be deleted by the framework calling us.
41 return std::move(TestConsumer);
45 std::unique_ptr<clang::ASTConsumer> TestConsumer;
48 class FindTopLevelDeclConsumer : public clang::ASTConsumer {
50 explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
51 : FoundTopLevelDecl(FoundTopLevelDecl) {}
52 virtual bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) {
53 *FoundTopLevelDecl = true;
57 bool * const FoundTopLevelDecl;
61 TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
62 bool FoundTopLevelDecl = false;
64 runToolOnCode(new TestAction(llvm::make_unique<FindTopLevelDeclConsumer>(
67 EXPECT_FALSE(FoundTopLevelDecl);
71 class FindClassDeclXConsumer : public clang::ASTConsumer {
73 FindClassDeclXConsumer(bool *FoundClassDeclX)
74 : FoundClassDeclX(FoundClassDeclX) {}
75 virtual bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) {
76 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(
78 if (Record->getName() == "X") {
79 *FoundClassDeclX = true;
85 bool *FoundClassDeclX;
87 bool FindClassDeclX(ASTUnit *AST) {
88 for (std::vector<Decl *>::iterator i = AST->top_level_begin(),
89 e = AST->top_level_end();
91 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) {
92 if (Record->getName() == "X") {
101 TEST(runToolOnCode, FindsClassDecl) {
102 bool FoundClassDeclX = false;
104 runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>(
107 EXPECT_TRUE(FoundClassDeclX);
109 FoundClassDeclX = false;
111 runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>(
114 EXPECT_FALSE(FoundClassDeclX);
117 TEST(buildASTFromCode, FindsClassDecl) {
118 std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;");
119 ASSERT_TRUE(AST.get());
120 EXPECT_TRUE(FindClassDeclX(AST.get()));
122 AST = buildASTFromCode("class Y;");
123 ASSERT_TRUE(AST.get());
124 EXPECT_FALSE(FindClassDeclX(AST.get()));
127 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
128 std::unique_ptr<FrontendActionFactory> Factory(
129 newFrontendActionFactory<SyntaxOnlyAction>());
130 std::unique_ptr<FrontendAction> Action(Factory->create());
131 EXPECT_TRUE(Action.get() != nullptr);
134 struct IndependentFrontendActionCreator {
135 std::unique_ptr<ASTConsumer> newASTConsumer() {
136 return llvm::make_unique<FindTopLevelDeclConsumer>(nullptr);
140 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
141 IndependentFrontendActionCreator Creator;
142 std::unique_ptr<FrontendActionFactory> Factory(
143 newFrontendActionFactory(&Creator));
144 std::unique_ptr<FrontendAction> Action(Factory->create());
145 EXPECT_TRUE(Action.get() != nullptr);
148 TEST(ToolInvocation, TestMapVirtualFile) {
149 IntrusiveRefCntPtr<clang::FileManager> Files(
150 new clang::FileManager(clang::FileSystemOptions()));
151 std::vector<std::string> Args;
152 Args.push_back("tool-executable");
153 Args.push_back("-Idef");
154 Args.push_back("-fsyntax-only");
155 Args.push_back("test.cpp");
156 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
158 Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
159 Invocation.mapVirtualFile("def/abc", "\n");
160 EXPECT_TRUE(Invocation.run());
163 TEST(ToolInvocation, TestVirtualModulesCompilation) {
164 // FIXME: Currently, this only tests that we don't exit with an error if a
165 // mapped module.map is found on the include path. In the future, expand this
166 // test to run a full modules enabled compilation, so we make sure we can
167 // rerun modules compilations with a virtual file system.
168 IntrusiveRefCntPtr<clang::FileManager> Files(
169 new clang::FileManager(clang::FileSystemOptions()));
170 std::vector<std::string> Args;
171 Args.push_back("tool-executable");
172 Args.push_back("-Idef");
173 Args.push_back("-fsyntax-only");
174 Args.push_back("test.cpp");
175 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
177 Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
178 Invocation.mapVirtualFile("def/abc", "\n");
179 // Add a module.map file in the include directory of our header, so we trigger
180 // the module.map header search logic.
181 Invocation.mapVirtualFile("def/module.map", "\n");
182 EXPECT_TRUE(Invocation.run());
185 struct VerifyEndCallback : public SourceFileCallbacks {
186 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
187 virtual bool handleBeginSource(CompilerInstance &CI,
188 StringRef Filename) override {
192 virtual void handleEndSource() override {
195 std::unique_ptr<ASTConsumer> newASTConsumer() {
196 return llvm::make_unique<FindTopLevelDeclConsumer>(&Matched);
198 unsigned BeginCalled;
203 #if !defined(LLVM_ON_WIN32)
204 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
205 VerifyEndCallback EndCallback;
207 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
208 std::vector<std::string> Sources;
209 Sources.push_back("/a.cc");
210 Sources.push_back("/b.cc");
211 ClangTool Tool(Compilations, Sources);
213 Tool.mapVirtualFile("/a.cc", "void a() {}");
214 Tool.mapVirtualFile("/b.cc", "void b() {}");
216 std::unique_ptr<FrontendActionFactory> Action(
217 newFrontendActionFactory(&EndCallback, &EndCallback));
218 Tool.run(Action.get());
220 EXPECT_TRUE(EndCallback.Matched);
221 EXPECT_EQ(2u, EndCallback.BeginCalled);
222 EXPECT_EQ(2u, EndCallback.EndCalled);
226 struct SkipBodyConsumer : public clang::ASTConsumer {
227 /// Skip the 'skipMe' function.
228 virtual bool shouldSkipFunctionBody(Decl *D) {
229 FunctionDecl *F = dyn_cast<FunctionDecl>(D);
230 return F && F->getNameAsString() == "skipMe";
234 struct SkipBodyAction : public clang::ASTFrontendAction {
235 virtual std::unique_ptr<ASTConsumer>
236 CreateASTConsumer(CompilerInstance &Compiler, StringRef) {
237 Compiler.getFrontendOpts().SkipFunctionBodies = true;
238 return llvm::make_unique<SkipBodyConsumer>();
242 TEST(runToolOnCode, TestSkipFunctionBody) {
243 EXPECT_TRUE(runToolOnCode(new SkipBodyAction,
244 "int skipMe() { an_error_here }"));
245 EXPECT_FALSE(runToolOnCode(new SkipBodyAction,
246 "int skipMeNot() { an_error_here }"));
249 TEST(runToolOnCodeWithArgs, TestNoDepFile) {
250 llvm::SmallString<32> DepFilePath;
252 llvm::sys::fs::createTemporaryFile("depfile", "d", DepFilePath));
253 std::vector<std::string> Args;
254 Args.push_back("-MMD");
255 Args.push_back("-MT");
256 Args.push_back(DepFilePath.str());
257 Args.push_back("-MF");
258 Args.push_back(DepFilePath.str());
259 EXPECT_TRUE(runToolOnCodeWithArgs(new SkipBodyAction, "", Args));
260 EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str()));
261 EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str()));
264 TEST(ClangToolTest, ArgumentAdjusters) {
265 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
267 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
268 Tool.mapVirtualFile("/a.cc", "void a() {}");
270 std::unique_ptr<FrontendActionFactory> Action(
271 newFrontendActionFactory<SyntaxOnlyAction>());
275 ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
276 [&Found, &Ran](const CommandLineArguments &Args) {
278 if (std::find(Args.begin(), Args.end(), "-fsyntax-only") != Args.end())
282 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
283 Tool.run(Action.get());
288 Tool.clearArgumentsAdjusters();
289 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
290 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
291 Tool.run(Action.get());
296 #ifndef LLVM_ON_WIN32
297 TEST(ClangToolTest, BuildASTs) {
298 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
300 std::vector<std::string> Sources;
301 Sources.push_back("/a.cc");
302 Sources.push_back("/b.cc");
303 ClangTool Tool(Compilations, Sources);
305 Tool.mapVirtualFile("/a.cc", "void a() {}");
306 Tool.mapVirtualFile("/b.cc", "void b() {}");
308 std::vector<std::unique_ptr<ASTUnit>> ASTs;
309 EXPECT_EQ(0, Tool.buildASTs(ASTs));
310 EXPECT_EQ(2u, ASTs.size());
313 struct TestDiagnosticConsumer : public DiagnosticConsumer {
314 TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
315 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
316 const Diagnostic &Info) {
317 ++NumDiagnosticsSeen;
319 unsigned NumDiagnosticsSeen;
322 TEST(ClangToolTest, InjectDiagnosticConsumer) {
323 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
324 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
325 Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
326 TestDiagnosticConsumer Consumer;
327 Tool.setDiagnosticConsumer(&Consumer);
328 std::unique_ptr<FrontendActionFactory> Action(
329 newFrontendActionFactory<SyntaxOnlyAction>());
330 Tool.run(Action.get());
331 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
334 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
335 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
336 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
337 Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
338 TestDiagnosticConsumer Consumer;
339 Tool.setDiagnosticConsumer(&Consumer);
340 std::vector<std::unique_ptr<ASTUnit>> ASTs;
341 Tool.buildASTs(ASTs);
342 EXPECT_EQ(1u, ASTs.size());
343 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
347 } // end namespace tooling
348 } // end namespace clang