• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
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"
22 #include <string>
23 
24 namespace clang {
25 namespace tooling {
26 
27 namespace {
28 /// Takes an ast consumer and returns it from CreateASTConsumer. This only
29 /// works with single translation unit compilations.
30 class TestAction : public clang::ASTFrontendAction {
31  public:
32   /// Takes ownership of TestConsumer.
TestAction(clang::ASTConsumer * TestConsumer)33   explicit TestAction(clang::ASTConsumer *TestConsumer)
34       : TestConsumer(TestConsumer) {}
35 
36  protected:
CreateASTConsumer(clang::CompilerInstance & compiler,StringRef dummy)37   virtual clang::ASTConsumer* CreateASTConsumer(
38       clang::CompilerInstance& compiler, StringRef dummy) {
39     /// TestConsumer will be deleted by the framework calling us.
40     return TestConsumer;
41   }
42 
43  private:
44   clang::ASTConsumer * const TestConsumer;
45 };
46 
47 class FindTopLevelDeclConsumer : public clang::ASTConsumer {
48  public:
FindTopLevelDeclConsumer(bool * FoundTopLevelDecl)49   explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
50       : FoundTopLevelDecl(FoundTopLevelDecl) {}
HandleTopLevelDecl(clang::DeclGroupRef DeclGroup)51   virtual bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) {
52     *FoundTopLevelDecl = true;
53     return true;
54   }
55  private:
56   bool * const FoundTopLevelDecl;
57 };
58 } // end namespace
59 
TEST(runToolOnCode,FindsNoTopLevelDeclOnEmptyCode)60 TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
61   bool FoundTopLevelDecl = false;
62   EXPECT_TRUE(runToolOnCode(
63       new TestAction(new FindTopLevelDeclConsumer(&FoundTopLevelDecl)), ""));
64   EXPECT_FALSE(FoundTopLevelDecl);
65 }
66 
67 namespace {
68 class FindClassDeclXConsumer : public clang::ASTConsumer {
69  public:
FindClassDeclXConsumer(bool * FoundClassDeclX)70   FindClassDeclXConsumer(bool *FoundClassDeclX)
71       : FoundClassDeclX(FoundClassDeclX) {}
HandleTopLevelDecl(clang::DeclGroupRef GroupRef)72   virtual bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) {
73     if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(
74             *GroupRef.begin())) {
75       if (Record->getName() == "X") {
76         *FoundClassDeclX = true;
77       }
78     }
79     return true;
80   }
81  private:
82   bool *FoundClassDeclX;
83 };
FindClassDeclX(ASTUnit * AST)84 bool FindClassDeclX(ASTUnit *AST) {
85   for (std::vector<Decl *>::iterator i = AST->top_level_begin(),
86                                      e = AST->top_level_end();
87        i != e; ++i) {
88     if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) {
89       if (Record->getName() == "X") {
90         return true;
91       }
92     }
93   }
94   return false;
95 }
96 } // end namespace
97 
TEST(runToolOnCode,FindsClassDecl)98 TEST(runToolOnCode, FindsClassDecl) {
99   bool FoundClassDeclX = false;
100   EXPECT_TRUE(runToolOnCode(new TestAction(
101       new FindClassDeclXConsumer(&FoundClassDeclX)), "class X;"));
102   EXPECT_TRUE(FoundClassDeclX);
103 
104   FoundClassDeclX = false;
105   EXPECT_TRUE(runToolOnCode(new TestAction(
106       new FindClassDeclXConsumer(&FoundClassDeclX)), "class Y;"));
107   EXPECT_FALSE(FoundClassDeclX);
108 }
109 
TEST(buildASTFromCode,FindsClassDecl)110 TEST(buildASTFromCode, FindsClassDecl) {
111   std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;");
112   ASSERT_TRUE(AST.get());
113   EXPECT_TRUE(FindClassDeclX(AST.get()));
114 
115   AST = buildASTFromCode("class Y;");
116   ASSERT_TRUE(AST.get());
117   EXPECT_FALSE(FindClassDeclX(AST.get()));
118 }
119 
TEST(newFrontendActionFactory,CreatesFrontendActionFactoryFromType)120 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
121   std::unique_ptr<FrontendActionFactory> Factory(
122       newFrontendActionFactory<SyntaxOnlyAction>());
123   std::unique_ptr<FrontendAction> Action(Factory->create());
124   EXPECT_TRUE(Action.get() != nullptr);
125 }
126 
127 struct IndependentFrontendActionCreator {
newASTConsumerclang::tooling::IndependentFrontendActionCreator128   ASTConsumer *newASTConsumer() {
129     return new FindTopLevelDeclConsumer(nullptr);
130   }
131 };
132 
TEST(newFrontendActionFactory,CreatesFrontendActionFactoryFromFactoryType)133 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
134   IndependentFrontendActionCreator Creator;
135   std::unique_ptr<FrontendActionFactory> Factory(
136       newFrontendActionFactory(&Creator));
137   std::unique_ptr<FrontendAction> Action(Factory->create());
138   EXPECT_TRUE(Action.get() != nullptr);
139 }
140 
TEST(ToolInvocation,TestMapVirtualFile)141 TEST(ToolInvocation, TestMapVirtualFile) {
142   IntrusiveRefCntPtr<clang::FileManager> Files(
143       new clang::FileManager(clang::FileSystemOptions()));
144   std::vector<std::string> Args;
145   Args.push_back("tool-executable");
146   Args.push_back("-Idef");
147   Args.push_back("-fsyntax-only");
148   Args.push_back("test.cpp");
149   clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
150                                             Files.get());
151   Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
152   Invocation.mapVirtualFile("def/abc", "\n");
153   EXPECT_TRUE(Invocation.run());
154 }
155 
TEST(ToolInvocation,TestVirtualModulesCompilation)156 TEST(ToolInvocation, TestVirtualModulesCompilation) {
157   // FIXME: Currently, this only tests that we don't exit with an error if a
158   // mapped module.map is found on the include path. In the future, expand this
159   // test to run a full modules enabled compilation, so we make sure we can
160   // rerun modules compilations with a virtual file system.
161   IntrusiveRefCntPtr<clang::FileManager> Files(
162       new clang::FileManager(clang::FileSystemOptions()));
163   std::vector<std::string> Args;
164   Args.push_back("tool-executable");
165   Args.push_back("-Idef");
166   Args.push_back("-fsyntax-only");
167   Args.push_back("test.cpp");
168   clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
169                                             Files.get());
170   Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
171   Invocation.mapVirtualFile("def/abc", "\n");
172   // Add a module.map file in the include directory of our header, so we trigger
173   // the module.map header search logic.
174   Invocation.mapVirtualFile("def/module.map", "\n");
175   EXPECT_TRUE(Invocation.run());
176 }
177 
178 struct VerifyEndCallback : public SourceFileCallbacks {
VerifyEndCallbackclang::tooling::VerifyEndCallback179   VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
handleBeginSourceclang::tooling::VerifyEndCallback180   virtual bool handleBeginSource(CompilerInstance &CI,
181                                  StringRef Filename) override {
182     ++BeginCalled;
183     return true;
184   }
handleEndSourceclang::tooling::VerifyEndCallback185   virtual void handleEndSource() {
186     ++EndCalled;
187   }
newASTConsumerclang::tooling::VerifyEndCallback188   ASTConsumer *newASTConsumer() {
189     return new FindTopLevelDeclConsumer(&Matched);
190   }
191   unsigned BeginCalled;
192   unsigned EndCalled;
193   bool Matched;
194 };
195 
196 #if !defined(LLVM_ON_WIN32)
TEST(newFrontendActionFactory,InjectsSourceFileCallbacks)197 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
198   VerifyEndCallback EndCallback;
199 
200   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
201   std::vector<std::string> Sources;
202   Sources.push_back("/a.cc");
203   Sources.push_back("/b.cc");
204   ClangTool Tool(Compilations, Sources);
205 
206   Tool.mapVirtualFile("/a.cc", "void a() {}");
207   Tool.mapVirtualFile("/b.cc", "void b() {}");
208 
209   std::unique_ptr<FrontendActionFactory> Action(
210       newFrontendActionFactory(&EndCallback, &EndCallback));
211   Tool.run(Action.get());
212 
213   EXPECT_TRUE(EndCallback.Matched);
214   EXPECT_EQ(2u, EndCallback.BeginCalled);
215   EXPECT_EQ(2u, EndCallback.EndCalled);
216 }
217 #endif
218 
219 struct SkipBodyConsumer : public clang::ASTConsumer {
220   /// Skip the 'skipMe' function.
shouldSkipFunctionBodyclang::tooling::SkipBodyConsumer221   virtual bool shouldSkipFunctionBody(Decl *D) {
222     FunctionDecl *F = dyn_cast<FunctionDecl>(D);
223     return F && F->getNameAsString() == "skipMe";
224   }
225 };
226 
227 struct SkipBodyAction : public clang::ASTFrontendAction {
CreateASTConsumerclang::tooling::SkipBodyAction228   virtual ASTConsumer *CreateASTConsumer(CompilerInstance &Compiler,
229                                          StringRef) {
230     Compiler.getFrontendOpts().SkipFunctionBodies = true;
231     return new SkipBodyConsumer;
232   }
233 };
234 
TEST(runToolOnCode,TestSkipFunctionBody)235 TEST(runToolOnCode, TestSkipFunctionBody) {
236   EXPECT_TRUE(runToolOnCode(new SkipBodyAction,
237                             "int skipMe() { an_error_here }"));
238   EXPECT_FALSE(runToolOnCode(new SkipBodyAction,
239                              "int skipMeNot() { an_error_here }"));
240 }
241 
TEST(runToolOnCodeWithArgs,TestNoDepFile)242 TEST(runToolOnCodeWithArgs, TestNoDepFile) {
243   llvm::SmallString<32> DepFilePath;
244   ASSERT_FALSE(
245       llvm::sys::fs::createTemporaryFile("depfile", "d", DepFilePath));
246   std::vector<std::string> Args;
247   Args.push_back("-MMD");
248   Args.push_back("-MT");
249   Args.push_back(DepFilePath.str());
250   Args.push_back("-MF");
251   Args.push_back(DepFilePath.str());
252   EXPECT_TRUE(runToolOnCodeWithArgs(new SkipBodyAction, "", Args));
253   EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str()));
254   EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str()));
255 }
256 
257 struct CheckSyntaxOnlyAdjuster: public ArgumentsAdjuster {
258   bool &Found;
259   bool &Ran;
260 
CheckSyntaxOnlyAdjusterclang::tooling::CheckSyntaxOnlyAdjuster261   CheckSyntaxOnlyAdjuster(bool &Found, bool &Ran) : Found(Found), Ran(Ran) { }
262 
263   virtual CommandLineArguments
Adjustclang::tooling::CheckSyntaxOnlyAdjuster264   Adjust(const CommandLineArguments &Args) override {
265     Ran = true;
266     for (unsigned I = 0, E = Args.size(); I != E; ++I) {
267       if (Args[I] == "-fsyntax-only") {
268         Found = true;
269         break;
270       }
271     }
272     return Args;
273   }
274 };
275 
TEST(ClangToolTest,ArgumentAdjusters)276 TEST(ClangToolTest, ArgumentAdjusters) {
277   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
278 
279   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
280   Tool.mapVirtualFile("/a.cc", "void a() {}");
281 
282   std::unique_ptr<FrontendActionFactory> Action(
283       newFrontendActionFactory<SyntaxOnlyAction>());
284 
285   bool Found = false;
286   bool Ran = false;
287   Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran));
288   Tool.run(Action.get());
289   EXPECT_TRUE(Ran);
290   EXPECT_TRUE(Found);
291 
292   Ran = Found = false;
293   Tool.clearArgumentsAdjusters();
294   Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran));
295   Tool.appendArgumentsAdjuster(new ClangSyntaxOnlyAdjuster());
296   Tool.run(Action.get());
297   EXPECT_TRUE(Ran);
298   EXPECT_FALSE(Found);
299 }
300 
301 #ifndef LLVM_ON_WIN32
TEST(ClangToolTest,BuildASTs)302 TEST(ClangToolTest, BuildASTs) {
303   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
304 
305   std::vector<std::string> Sources;
306   Sources.push_back("/a.cc");
307   Sources.push_back("/b.cc");
308   ClangTool Tool(Compilations, Sources);
309 
310   Tool.mapVirtualFile("/a.cc", "void a() {}");
311   Tool.mapVirtualFile("/b.cc", "void b() {}");
312 
313   std::vector<std::unique_ptr<ASTUnit>> ASTs;
314   EXPECT_EQ(0, Tool.buildASTs(ASTs));
315   EXPECT_EQ(2u, ASTs.size());
316 }
317 
318 struct TestDiagnosticConsumer : public DiagnosticConsumer {
TestDiagnosticConsumerclang::tooling::TestDiagnosticConsumer319   TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
HandleDiagnosticclang::tooling::TestDiagnosticConsumer320   virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
321                                 const Diagnostic &Info) {
322     ++NumDiagnosticsSeen;
323   }
324   unsigned NumDiagnosticsSeen;
325 };
326 
TEST(ClangToolTest,InjectDiagnosticConsumer)327 TEST(ClangToolTest, InjectDiagnosticConsumer) {
328   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
329   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
330   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
331   TestDiagnosticConsumer Consumer;
332   Tool.setDiagnosticConsumer(&Consumer);
333   std::unique_ptr<FrontendActionFactory> Action(
334       newFrontendActionFactory<SyntaxOnlyAction>());
335   Tool.run(Action.get());
336   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
337 }
338 
TEST(ClangToolTest,InjectDiagnosticConsumerInBuildASTs)339 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
340   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
341   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
342   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
343   TestDiagnosticConsumer Consumer;
344   Tool.setDiagnosticConsumer(&Consumer);
345   std::vector<std::unique_ptr<ASTUnit>> ASTs;
346   Tool.buildASTs(ASTs);
347   EXPECT_EQ(1u, ASTs.size());
348   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
349 }
350 #endif
351 
352 } // end namespace tooling
353 } // end namespace clang
354