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