Clang Project

clang_source_code/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
26namespace clang {
27namespace tooling {
28
29namespace {
30
31// This traverses the AST and outputs function name as key and "1" as value for
32// each function declaration.
33class ASTConsumerWithResult
34    : public ASTConsumer,
35      public RecursiveASTVisitor<ASTConsumerWithResult> {
36public:
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
55private:
56  ExecutionContext *const Context;
57};
58
59class ReportResultAction : public ASTFrontendAction {
60public:
61  explicit ReportResultAction(ExecutionContext *Context) : Context(Context) {
62    assert(Context != nullptr);
63  }
64
65protected:
66  std::unique_ptr<clang::ASTConsumer>
67  CreateASTConsumer(clang::CompilerInstance &compiler,
68                    StringRef /* dummy */) override {
69    std::unique_ptr<clang::ASTConsumerast_consumer{
70        new ASTConsumerWithResult(Context)};
71    return ast_consumer;
72  }
73
74private:
75  ExecutionContext *const Context;
76};
77
78class ReportResultActionFactory : public FrontendActionFactory {
79public:
80  ReportResultActionFactory(ExecutionContext *Context) : Context(Context) {}
81  FrontendAction *create() override { return new ReportResultAction(Context); }
82
83private:
84  ExecutionContext *const Context;
85};
86
87// namespace
88
89class TestToolExecutor : public ToolExecutor {
90public:
91  static const char *ExecutorName;
92
93  TestToolExecutor(CommonOptionsParser Options)
94      : OptionsParser(std::move(Options)) {}
95
96  StringRef getExecutorName() const override { return ExecutorName; }
97
98  bool isSingleProcess() const override { return true; }
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::stringgetSourcePaths() const {
111    return OptionsParser.getSourcePathList();
112  }
113
114  void mapVirtualFile(StringRef FilePathStringRef Content) override {
115    VFS[FilePath] = Content;
116  }
117
118private:
119  CommonOptionsParser OptionsParser;
120  std::string SourcePaths;
121  std::map<std::stringstd::stringVFS;
122};
123
124const char *TestToolExecutor::ExecutorName = "test-executor";
125
126class TestToolExecutorPlugin : public ToolExecutorPlugin {
127public:
128  llvm::Expected<std::unique_ptr<ToolExecutor>>
129  create(CommonOptionsParser &OptionsParser) override {
130    return llvm::make_unique<TestToolExecutor>(std::move(OptionsParser));
131  }
132};
133
134static ToolExecutorPluginRegistry::Add<TestToolExecutorPlugin>
135    X("test-executor""Plugin for TestToolExecutor.");
136
137llvm::cl::OptionCategory TestCategory("execution-test options");
138
139TEST(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
148TEST(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
164TEST(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
174TEST(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
184TEST(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
195TEST(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
208TEST(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      [](StringRefStringRef Value) { EXPECT_EQ("::/1", Value); });
226}
227
228class FixedCompilationDatabaseWithFiles : public CompilationDatabase {
229public:
230  FixedCompilationDatabaseWithFiles(Twine Directory,
231                                    ArrayRef<std::stringFiles,
232                                    ArrayRef<std::stringCommandLine)
233      : FixedCompilations(Directory, CommandLine), Files(Files) {}
234
235  std::vector<CompileCommand>
236  getCompileCommands(StringRef FilePathconst override {
237    return FixedCompilations.getCompileCommands(FilePath);
238  }
239
240  std::vector<std::stringgetAllFiles() const override { return Files; }
241
242private:
243  FixedCompilationDatabase FixedCompilations;
244  std::vector<std::stringFiles;
245};
246
247MATCHER_P(Named, Name, "") { return arg.first == Name; }
248
249TEST(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
268TEST(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
298
clang::tooling::TestToolExecutor::getExecutorName
clang::tooling::TestToolExecutor::isSingleProcess
clang::tooling::TestToolExecutor::getExecutionContext
clang::tooling::TestToolExecutor::getToolResults
clang::tooling::TestToolExecutor::mapVirtualFile
clang::tooling::TestToolExecutorPlugin::create
clang::tooling::FixedCompilationDatabaseWithFiles::getCompileCommands
clang::tooling::FixedCompilationDatabaseWithFiles::getAllFiles