Changed FrontendActionFactory::create to return a std::unique_ptr
[lldb.git] / clang / unittests / Tooling / ExecutionTest.cpp
1 //===- unittest/Tooling/ExecutionTest.cpp - Tool execution 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/Execution.h"
10 #include "clang/AST/ASTConsumer.h"
11 #include "clang/AST/DeclCXX.h"
12 #include "clang/AST/RecursiveASTVisitor.h"
13 #include "clang/Frontend/ASTUnit.h"
14 #include "clang/Frontend/FrontendAction.h"
15 #include "clang/Frontend/FrontendActions.h"
16 #include "clang/Tooling/AllTUsExecution.h"
17 #include "clang/Tooling/CompilationDatabase.h"
18 #include "clang/Tooling/StandaloneExecution.h"
19 #include "clang/Tooling/ToolExecutorPluginRegistry.h"
20 #include "clang/Tooling/Tooling.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include <algorithm>
24 #include <string>
25
26 namespace clang {
27 namespace tooling {
28
29 namespace {
30
31 // This traverses the AST and outputs function name as key and "1" as value for
32 // each function declaration.
33 class ASTConsumerWithResult
34     : public ASTConsumer,
35       public RecursiveASTVisitor<ASTConsumerWithResult> {
36 public:
37   using ASTVisitor = RecursiveASTVisitor<ASTConsumerWithResult>;
38
39   explicit ASTConsumerWithResult(ExecutionContext *Context) : Context(Context) {
40     assert(Context != nullptr);
41   }
42
43   void HandleTranslationUnit(clang::ASTContext &Context) override {
44     TraverseDecl(Context.getTranslationUnitDecl());
45   }
46
47   bool TraverseFunctionDecl(clang::FunctionDecl *Decl) {
48     Context->reportResult(Decl->getNameAsString(),
49                           Context->getRevision() + ":" + Context->getCorpus() +
50                               ":" + Context->getCurrentCompilationUnit() +
51                               "/1");
52     return ASTVisitor::TraverseFunctionDecl(Decl);
53   }
54
55 private:
56   ExecutionContext *const Context;
57 };
58
59 class ReportResultAction : public ASTFrontendAction {
60 public:
61   explicit ReportResultAction(ExecutionContext *Context) : Context(Context) {
62     assert(Context != nullptr);
63   }
64
65 protected:
66   std::unique_ptr<clang::ASTConsumer>
67   CreateASTConsumer(clang::CompilerInstance &compiler,
68                     StringRef /* dummy */) override {
69     std::unique_ptr<clang::ASTConsumer> ast_consumer{
70         new ASTConsumerWithResult(Context)};
71     return ast_consumer;
72   }
73
74 private:
75   ExecutionContext *const Context;
76 };
77
78 class ReportResultActionFactory : public FrontendActionFactory {
79 public:
80   ReportResultActionFactory(ExecutionContext *Context) : Context(Context) {}
81   std::unique_ptr<FrontendAction> create() override {
82     return std::make_unique<ReportResultAction>(Context);
83   }
84
85 private:
86   ExecutionContext *const Context;
87 };
88
89 } // namespace
90
91 class TestToolExecutor : public ToolExecutor {
92 public:
93   static const char *ExecutorName;
94
95   TestToolExecutor(CommonOptionsParser Options)
96       : OptionsParser(std::move(Options)) {}
97
98   StringRef getExecutorName() const override { return ExecutorName; }
99
100   llvm::Error
101   execute(llvm::ArrayRef<std::pair<std::unique_ptr<FrontendActionFactory>,
102                                    ArgumentsAdjuster>>) override {
103     return llvm::Error::success();
104   }
105
106   ExecutionContext *getExecutionContext() override { return nullptr; };
107
108   ToolResults *getToolResults() override { return nullptr; }
109
110   llvm::ArrayRef<std::string> getSourcePaths() const {
111     return OptionsParser.getSourcePathList();
112   }
113
114   void mapVirtualFile(StringRef FilePath, StringRef Content) override {
115     VFS[FilePath] = Content;
116   }
117
118 private:
119   CommonOptionsParser OptionsParser;
120   std::string SourcePaths;
121   std::map<std::string, std::string> VFS;
122 };
123
124 const char *TestToolExecutor::ExecutorName = "test-executor";
125
126 class TestToolExecutorPlugin : public ToolExecutorPlugin {
127 public:
128   llvm::Expected<std::unique_ptr<ToolExecutor>>
129   create(CommonOptionsParser &OptionsParser) override {
130     return std::make_unique<TestToolExecutor>(std::move(OptionsParser));
131   }
132 };
133
134 static ToolExecutorPluginRegistry::Add<TestToolExecutorPlugin>
135     X("test-executor", "Plugin for TestToolExecutor.");
136
137 llvm::cl::OptionCategory TestCategory("execution-test options");
138
139 TEST(CreateToolExecutorTest, FailedCreateExecutorUndefinedFlag) {
140   std::vector<const char *> argv = {"prog", "--fake_flag_no_no_no", "f"};
141   int argc = argv.size();
142   auto Executor = internal::createExecutorFromCommandLineArgsImpl(
143       argc, &argv[0], TestCategory);
144   ASSERT_FALSE((bool)Executor);
145   llvm::consumeError(Executor.takeError());
146 }
147
148 TEST(CreateToolExecutorTest, RegisterFlagsBeforeReset) {
149   llvm::cl::opt<std::string> BeforeReset(
150       "before_reset", llvm::cl::desc("Defined before reset."),
151       llvm::cl::init(""));
152
153   llvm::cl::ResetAllOptionOccurrences();
154
155   std::vector<const char *> argv = {"prog", "--before_reset=set", "f"};
156   int argc = argv.size();
157   auto Executor = internal::createExecutorFromCommandLineArgsImpl(
158       argc, &argv[0], TestCategory);
159   ASSERT_TRUE((bool)Executor);
160   EXPECT_EQ(BeforeReset, "set");
161   BeforeReset.removeArgument();
162 }
163
164 TEST(CreateToolExecutorTest, CreateStandaloneToolExecutor) {
165   std::vector<const char *> argv = {"prog", "standalone.cpp"};
166   int argc = argv.size();
167   auto Executor = internal::createExecutorFromCommandLineArgsImpl(
168       argc, &argv[0], TestCategory);
169   ASSERT_TRUE((bool)Executor);
170   EXPECT_EQ(Executor->get()->getExecutorName(),
171             StandaloneToolExecutor::ExecutorName);
172 }
173
174 TEST(CreateToolExecutorTest, CreateTestToolExecutor) {
175   std::vector<const char *> argv = {"prog", "test.cpp",
176                                     "--executor=test-executor"};
177   int argc = argv.size();
178   auto Executor = internal::createExecutorFromCommandLineArgsImpl(
179       argc, &argv[0], TestCategory);
180   ASSERT_TRUE((bool)Executor);
181   EXPECT_EQ(Executor->get()->getExecutorName(), TestToolExecutor::ExecutorName);
182 }
183
184 TEST(StandaloneToolTest, SynctaxOnlyActionOnSimpleCode) {
185   FixedCompilationDatabase Compilations(".", std::vector<std::string>());
186   StandaloneToolExecutor Executor(Compilations,
187                                   std::vector<std::string>(1, "a.cc"));
188   Executor.mapVirtualFile("a.cc", "int x = 0;");
189
190   auto Err = Executor.execute(newFrontendActionFactory<SyntaxOnlyAction>(),
191                               getClangSyntaxOnlyAdjuster());
192   ASSERT_TRUE(!Err);
193 }
194
195 TEST(StandaloneToolTest, SimpleAction) {
196   FixedCompilationDatabase Compilations(".", std::vector<std::string>());
197   StandaloneToolExecutor Executor(Compilations,
198                                   std::vector<std::string>(1, "a.cc"));
199   Executor.mapVirtualFile("a.cc", "int x = 0;");
200
201   auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(
202       new ReportResultActionFactory(Executor.getExecutionContext())));
203   ASSERT_TRUE(!Err);
204   auto KVs = Executor.getToolResults()->AllKVResults();
205   ASSERT_EQ(KVs.size(), 0u);
206 }
207
208 TEST(StandaloneToolTest, SimpleActionWithResult) {
209   FixedCompilationDatabase Compilations(".", std::vector<std::string>());
210   StandaloneToolExecutor Executor(Compilations,
211                                   std::vector<std::string>(1, "a.cc"));
212   Executor.mapVirtualFile("a.cc", "int x = 0; void f() {}");
213
214   auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(
215       new ReportResultActionFactory(Executor.getExecutionContext())));
216   ASSERT_TRUE(!Err);
217   auto KVs = Executor.getToolResults()->AllKVResults();
218   ASSERT_EQ(KVs.size(), 1u);
219   EXPECT_EQ("f", KVs[0].first);
220   // Currently the standlone executor returns empty corpus, revision, and
221   // compilation unit.
222   EXPECT_EQ("::/1", KVs[0].second);
223
224   Executor.getToolResults()->forEachResult(
225       [](StringRef, StringRef Value) { EXPECT_EQ("::/1", Value); });
226 }
227
228 class FixedCompilationDatabaseWithFiles : public CompilationDatabase {
229 public:
230   FixedCompilationDatabaseWithFiles(Twine Directory,
231                                     ArrayRef<std::string> Files,
232                                     ArrayRef<std::string> CommandLine)
233       : FixedCompilations(Directory, CommandLine), Files(Files) {}
234
235   std::vector<CompileCommand>
236   getCompileCommands(StringRef FilePath) const override {
237     return FixedCompilations.getCompileCommands(FilePath);
238   }
239
240   std::vector<std::string> getAllFiles() const override { return Files; }
241
242 private:
243   FixedCompilationDatabase FixedCompilations;
244   std::vector<std::string> Files;
245 };
246
247 MATCHER_P(Named, Name, "") { return arg.first == Name; }
248
249 TEST(AllTUsToolTest, AFewFiles) {
250   FixedCompilationDatabaseWithFiles Compilations(
251       ".", {"a.cc", "b.cc", "c.cc", "ignore.cc"}, std::vector<std::string>());
252   AllTUsToolExecutor Executor(Compilations, /*ThreadCount=*/0);
253   Filter.setValue("[a-c].cc");
254   Executor.mapVirtualFile("a.cc", "void x() {}");
255   Executor.mapVirtualFile("b.cc", "void y() {}");
256   Executor.mapVirtualFile("c.cc", "void z() {}");
257   Executor.mapVirtualFile("ignore.cc", "void d() {}");
258
259   auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(
260       new ReportResultActionFactory(Executor.getExecutionContext())));
261   ASSERT_TRUE(!Err);
262   EXPECT_THAT(
263       Executor.getToolResults()->AllKVResults(),
264       ::testing::UnorderedElementsAre(Named("x"), Named("y"), Named("z")));
265   Filter.setValue(".*"); // reset to default value.
266 }
267
268 TEST(AllTUsToolTest, ManyFiles) {
269   unsigned NumFiles = 100;
270   std::vector<std::string> Files;
271   std::map<std::string, std::string> FileToContent;
272   std::vector<std::string> ExpectedSymbols;
273   for (unsigned i = 1; i <= NumFiles; ++i) {
274     std::string File = "f" + std::to_string(i) + ".cc";
275     std::string Symbol = "looong_function_name_" + std::to_string(i);
276     Files.push_back(File);
277     FileToContent[File] = "void " + Symbol + "() {}";
278     ExpectedSymbols.push_back(Symbol);
279   }
280   FixedCompilationDatabaseWithFiles Compilations(".", Files,
281                                                  std::vector<std::string>());
282   AllTUsToolExecutor Executor(Compilations, /*ThreadCount=*/0);
283   for (const auto &FileAndContent : FileToContent) {
284     Executor.mapVirtualFile(FileAndContent.first, FileAndContent.second);
285   }
286
287   auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(
288       new ReportResultActionFactory(Executor.getExecutionContext())));
289   ASSERT_TRUE(!Err);
290   std::vector<std::string> Results;
291   Executor.getToolResults()->forEachResult(
292       [&](StringRef Name, StringRef) { Results.push_back(Name); });
293   EXPECT_THAT(ExpectedSymbols, ::testing::UnorderedElementsAreArray(Results));
294 }
295
296 } // end namespace tooling
297 } // end namespace clang